mirror of
https://github.com/Balshgit/public.git
synced 2025-09-11 18:00:42 +03:00
github stars project celery-rabbit
This commit is contained in:
commit
70bff65fc0
1
celery-rabbit-example/.gitignore
vendored
Normal file
1
celery-rabbit-example/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
!.env
|
34
celery-rabbit-example/Dockerfile
Normal file
34
celery-rabbit-example/Dockerfile
Normal file
@ -0,0 +1,34 @@
|
||||
FROM python:3.8.6-buster
|
||||
|
||||
ENV PYTHONFAULTHANDLER=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONHASHSEED=random \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
# pip:
|
||||
PIP_NO_CACHE_DIR=off \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||
PIP_DEFAULT_TIMEOUT=100
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
bash \
|
||||
build-essential \
|
||||
curl \
|
||||
gettext \
|
||||
git \
|
||||
libpq-dev \
|
||||
nano
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
# Copy and install dependencies:
|
||||
COPY requirements.txt /code/
|
||||
RUN python -m pip install --upgrade pip
|
||||
RUN pip install --no-cache-dir -r /code/requirements.txt
|
||||
|
||||
# Copy source files:
|
||||
COPY . /code/
|
||||
# COPY app.py /code/
|
||||
|
||||
|
||||
|
6
celery-rabbit-example/README.md
Normal file
6
celery-rabbit-example/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# celery first example
|
||||
|
||||
Steps:
|
||||
1. Run `docker-compose up`
|
||||
2. Show logs
|
||||
3. In a new terminal run `docker-compose exec worker python`
|
3
celery-rabbit-example/celery_config/__init__.py
Normal file
3
celery-rabbit-example/celery_config/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# from app_celery import app as my_celery_app
|
||||
#
|
||||
# __all__ = ('my_celery_app', )
|
25
celery-rabbit-example/celery_config/app_celery.py
Normal file
25
celery-rabbit-example/celery_config/app_celery.py
Normal file
@ -0,0 +1,25 @@
|
||||
from celery import Celery
|
||||
from pathlib import Path
|
||||
from decouple import AutoConfig
|
||||
|
||||
BASE_DIR = Path.cwd().parent
|
||||
config = AutoConfig(search_path=BASE_DIR.joinpath('config'))
|
||||
|
||||
|
||||
RABBITMQ_DEFAULT_USER = config('RABBITMQ_DEFAULT_USER')
|
||||
RABBITMQ_DEFAULT_PASS = config('RABBITMQ_DEFAULT_PASS')
|
||||
RABBITMQ_PORT = config('RABBITMQ_PORT', cast=int, default=5672)
|
||||
RABBITMQ_HOST = config('RABBITMQ_HOST')
|
||||
|
||||
|
||||
app_celery_instance = Celery(
|
||||
'tasks',
|
||||
broker='amqp://{login}:{password}@{host}:{port}'.format(
|
||||
login=RABBITMQ_DEFAULT_USER,
|
||||
password=RABBITMQ_DEFAULT_PASS,
|
||||
host=RABBITMQ_HOST,
|
||||
port=RABBITMQ_PORT,
|
||||
),
|
||||
# TODO: try to get async results with and without backend configured
|
||||
backend='rpc://',
|
||||
)
|
6
celery-rabbit-example/config/.env
Normal file
6
celery-rabbit-example/config/.env
Normal file
@ -0,0 +1,6 @@
|
||||
# RabbitMQ settings:
|
||||
|
||||
RABBITMQ_DEFAULT_USER=rabbit_admin
|
||||
RABBITMQ_DEFAULT_PASS=mypass
|
||||
RABBITMQ_PORT=5672
|
||||
RABBITMQ_HOST=rabbitmq_host
|
28
celery-rabbit-example/docker-compose.yml
Normal file
28
celery-rabbit-example/docker-compose.yml
Normal file
@ -0,0 +1,28 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
|
||||
rabbitmq:
|
||||
hostname: rabbitmq_host
|
||||
image: 'rabbitmq:3.8.18-management-alpine'
|
||||
container_name: first_rabbit
|
||||
env_file: config/.env
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8080:15672
|
||||
- 5672:5672
|
||||
|
||||
worker:
|
||||
container_name: first_celery
|
||||
build: .
|
||||
command: celery --app=my_app:app_celery_instance worker --loglevel=INFO
|
||||
env_file: config/.env
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: celery_network
|
||||
driver: bridge
|
||||
|
14
celery-rabbit-example/my_app.py
Normal file
14
celery-rabbit-example/my_app.py
Normal file
@ -0,0 +1,14 @@
|
||||
from celery_config.app_celery import app_celery_instance
|
||||
|
||||
|
||||
@app_celery_instance.task
|
||||
def add(first: int, second: int) -> int:
|
||||
print(first + second)
|
||||
return first + second
|
||||
|
||||
|
||||
# TODO: try with `@app.task(throws=(ZeroDivisionError,))`
|
||||
@app_celery_instance.task
|
||||
def div(first: int, second: int) -> float:
|
||||
# TODO: show how errors work
|
||||
return first / second
|
2
celery-rabbit-example/requirements.txt
Normal file
2
celery-rabbit-example/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
celery==5.0.2
|
||||
python-decouple==3.3
|
67
new-github-repos/.dockerignore
Normal file
67
new-github-repos/.dockerignore
Normal file
@ -0,0 +1,67 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
docker-compose.override.yml
|
||||
docker/docker-compose.prod.yml
|
||||
|
||||
# JetBrains
|
||||
.idea/
|
25
new-github-repos/.editorconfig
Normal file
25
new-github-repos/.editorconfig
Normal file
@ -0,0 +1,25 @@
|
||||
# Check http://editorconfig.org for more information
|
||||
# This is the main config file for this project:
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.pyi]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
247
new-github-repos/.gitignore
vendored
Normal file
247
new-github-repos/.gitignore
vendored
Normal file
@ -0,0 +1,247 @@
|
||||
#### joe made this: https://goel.io/joe
|
||||
|
||||
# Git style-guide:
|
||||
# https://github.com/agis-/git-style-guide
|
||||
|
||||
#####=== OSX ===#####
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
#####=== Windows ===#####
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
#####=== Python ===#####
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
#####=== Sass ===#####
|
||||
|
||||
.sass-cache
|
||||
*.css.map
|
||||
|
||||
#####=== Yeoman ===#####
|
||||
|
||||
node_modules/
|
||||
bower_components/
|
||||
*.log
|
||||
|
||||
build/
|
||||
dist/
|
||||
|
||||
#####=== Vagrant ===#####
|
||||
.vagrant/
|
||||
|
||||
#####=== Node ===#####
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
|
||||
node_modules
|
||||
|
||||
# Debug log from npm
|
||||
npm-debug.log
|
||||
|
||||
#### jetbrains ####
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
# You can uncomment these lines to enable configuration sharing between
|
||||
# team members, or you can restrict `.idea/` folder at all (default).
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/**/workspace.xml
|
||||
# .idea/**/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# # Sensitive or high-churn files:
|
||||
# .idea/**/dataSources/
|
||||
# .idea/**/dataSources.ids
|
||||
# .idea/**/dataSources.xml
|
||||
# .idea/**/dataSources.local.xml
|
||||
# .idea/**/sqlDataSources.xml
|
||||
# .idea/**/dynamic.xml
|
||||
# .idea/**/uiDesigner.xml
|
||||
|
||||
# # Gradle:
|
||||
# .idea/**/gradle.xml
|
||||
# .idea/**/libraries
|
||||
|
||||
# # Mongo Explorer plugin:
|
||||
# .idea/**/mongoSettings.xml
|
||||
|
||||
# # Cursive Clojure plugin
|
||||
# .idea/replstate.xml
|
||||
|
||||
# Restrict `.idea/` folder at all:
|
||||
.idea/
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
|
||||
#####=== Custom ===#####
|
||||
# Directories:
|
||||
media/
|
||||
.static/
|
||||
/static/
|
||||
|
||||
# File types:
|
||||
*.sqlite3
|
||||
# .db
|
||||
|
||||
|
||||
# Configuration file with private data:
|
||||
# *.env
|
||||
# .env
|
||||
|
||||
# Local settings files:
|
||||
*local.py
|
||||
|
||||
# Deploy files for Docker:
|
||||
docker-compose.deploy.yml
|
||||
|
||||
# Certificates:
|
||||
docker/caddy/certs/
|
||||
|
||||
# Artifacts:
|
||||
.gitlab/.svn/
|
||||
artifacts/
|
||||
|
||||
# mypy:
|
||||
.mypy_cache/
|
||||
|
||||
# pytest:
|
||||
.pytest_cache/
|
||||
|
||||
# ipython:
|
||||
.ipython/
|
96
new-github-repos/.gitlab-ci.yml
Normal file
96
new-github-repos/.gitlab-ci.yml
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
|
||||
variables:
|
||||
GROUP_NAME: "balsh"
|
||||
PROJECT_NAME: "github-repos"
|
||||
REGISTRY: "registry.gitlab.com"
|
||||
IMAGE_FULL_NAME: "${REGISTRY}/${GROUP_NAME}/${PROJECT_NAME}"
|
||||
|
||||
|
||||
# Base scripts
|
||||
# ============
|
||||
|
||||
.docker:
|
||||
# We use a custom dind image that is based on raw `docker`,
|
||||
# it has all the dependencies required.
|
||||
# By using it we reduce the build time significantly.
|
||||
# You can fallback to use raw `docker` image, see:
|
||||
# https://github.com/wemake-services/wemake-dind/
|
||||
image: wemakeservices/wemake-dind:latest
|
||||
interruptible: true
|
||||
services:
|
||||
- docker:dind
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
before_script: &docker-before-script
|
||||
# Making sure we are in the right directory, does nothing by default:
|
||||
- pwd && echo "$CI_PROJECT_DIR" && cd "$CI_PROJECT_DIR"
|
||||
# Creating `.env` configuration file:
|
||||
- dump-env -t config/.env.template -p 'SECRET_' > config/.env
|
||||
# Login into Docker registry:
|
||||
- echo "$CI_JOB_TOKEN" | docker login "$REGISTRY"
|
||||
-u gitlab-ci-token --password-stdin
|
||||
# Debug information:
|
||||
- docker info && docker-compose --version && git --version
|
||||
|
||||
|
||||
# Test scripts
|
||||
# ============
|
||||
|
||||
test:
|
||||
stage: test
|
||||
extends: .docker
|
||||
before_script:
|
||||
- *docker-before-script
|
||||
# Pulling cache:
|
||||
- docker pull "${IMAGE_FULL_NAME}:dev" || true
|
||||
- docker tag "${IMAGE_FULL_NAME}:dev" "${PROJECT_NAME}:dev" || true
|
||||
script:
|
||||
# Checking config:
|
||||
- docker-compose -f docker-compose.yml
|
||||
-f docker/docker-compose.prod.yml config --quiet
|
||||
|
||||
# The logic itself:
|
||||
- docker-compose build
|
||||
- docker-compose run --user=root --rm web sh ./docker/ci.sh
|
||||
- disl "${PROJECT_NAME}:dev" 950MiB
|
||||
|
||||
# Pushing back the result for future runs:
|
||||
- docker tag "${PROJECT_NAME}:dev" "${IMAGE_FULL_NAME}:dev"
|
||||
- docker push "${IMAGE_FULL_NAME}:dev"
|
||||
only:
|
||||
- merge_requests
|
||||
|
||||
|
||||
# Release scripts
|
||||
# ===============
|
||||
|
||||
# Releasing image, when in `master` branch,
|
||||
# can be replaced with `kira-release` bot:
|
||||
# https://github.com/wemake-services/kira-release
|
||||
release-image:
|
||||
extends: .docker
|
||||
stage: deploy
|
||||
allow_failure: false
|
||||
before_script:
|
||||
# Build local image to be released to gitlab registry,
|
||||
# modify it to suite your needs as you wish.
|
||||
# We only care about the name of the image:
|
||||
- *docker-before-script
|
||||
|
||||
# Now we need the latest images for cache and improved build times:
|
||||
- docker pull "${IMAGE_FULL_NAME}:latest" || true
|
||||
- docker pull "${IMAGE_FULL_NAME}:dev" || true
|
||||
# Create correct tags:
|
||||
- docker tag "${IMAGE_FULL_NAME}:latest" "${PROJECT_NAME}:latest" || true
|
||||
- docker tag "${IMAGE_FULL_NAME}:dev" "${PROJECT_NAME}:dev" || true
|
||||
|
||||
# Building the image itself:
|
||||
- docker-compose -f docker-compose.yml
|
||||
-f docker/docker-compose.prod.yml build
|
||||
script:
|
||||
- docker push "${IMAGE_FULL_NAME}:latest"
|
||||
only:
|
||||
- master
|
||||
environment:
|
||||
name: production # used to track time with 'cycle analytics'
|
1
new-github-repos/.python-version
Normal file
1
new-github-repos/.python-version
Normal file
@ -0,0 +1 @@
|
||||
3.8.9
|
0
new-github-repos/CHANGELOG.md
Normal file
0
new-github-repos/CHANGELOG.md
Normal file
32
new-github-repos/README.md
Normal file
32
new-github-repos/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# github-repos
|
||||
|
||||
github repos
|
||||
|
||||
This project was generated with [`wemake-django-template`](https://github.com/wemake-services/wemake-django-template). Current template version is: [281e5b90040a710092c5be2dbd6b381c464cdf87](https://github.com/wemake-services/wemake-django-template/tree/281e5b90040a710092c5be2dbd6b381c464cdf87). See what is [updated](https://github.com/wemake-services/wemake-django-template/compare/281e5b90040a710092c5be2dbd6b381c464cdf87...master) since then.
|
||||
|
||||
|
||||
[](https://wemake.services)
|
||||
[](https://github.com/wemake-services/wemake-python-styleguide)
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need:
|
||||
|
||||
- `python3.8` (see `pyproject.toml` for full version)
|
||||
- `postgresql` with version `9.6`
|
||||
- `docker` with [version at least](https://docs.docker.com/compose/compose-file/#compose-and-docker-compatibility-matrix) `18.02`
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
When developing locally, we use:
|
||||
|
||||
- [`editorconfig`](http://editorconfig.org/) plugin (**required**)
|
||||
- [`poetry`](https://github.com/python-poetry/poetry) (**required**)
|
||||
- `pycharm 2017+` or `vscode`
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation is available here: [`docs/`](docs).
|
43
new-github-repos/config/.env
Normal file
43
new-github-repos/config/.env
Normal file
@ -0,0 +1,43 @@
|
||||
# === General ===
|
||||
|
||||
DOMAIN_NAME=localhost
|
||||
TLS_EMAIL=webmaster@localhost
|
||||
|
||||
|
||||
# === Django ===
|
||||
# Generate yours with:
|
||||
# python3 -c 'from django.utils.crypto import get_random_string; print(get_random_string(50))'
|
||||
|
||||
DJANGO_SECRET_KEY=I^[!b6gyNlXmaI,/{tagz+>:4V]%HJNW(=(@:*T~)g-t47tc7y
|
||||
|
||||
|
||||
# === Database ===
|
||||
|
||||
# These variables are special, since they are consumed
|
||||
# by both django and postgres docker image.
|
||||
# Cannot be renamed if you use postgres in docker.
|
||||
# See: https://hub.docker.com/_/postgres
|
||||
|
||||
POSTGRES_DB=github-repos
|
||||
POSTGRES_USER=github-admin
|
||||
POSTGRES_PASSWORD=admin_password
|
||||
|
||||
# Used only by django:
|
||||
DJANGO_DATABASE_HOST=db
|
||||
DJANGO_DATABASE_PORT=5432
|
||||
|
||||
# ==== Email =======
|
||||
|
||||
EMAIL_HOST=smtp.yandex.ru
|
||||
EMAIL_HOST_USER=balsh-django@yandex.ru
|
||||
EMAIL_HOST_PASSWORD=nifwotooabmfauld
|
||||
EMAIL_PORT=465
|
||||
EMAIL_USE_SSL=True
|
||||
EMAIL_USE_TLS=False
|
||||
|
||||
# =======RabbitMQ=======
|
||||
|
||||
RABBITMQ_DEFAULT_USER=rabbit_admin
|
||||
RABBITMQ_DEFAULT_PASS=mypass
|
||||
RABBITMQ_PORT=5672
|
||||
RABBITMQ_HOST=rabbitmq_docker
|
70
new-github-repos/docker-compose.yml
Normal file
70
new-github-repos/docker-compose.yml
Normal file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: "postgres:12"
|
||||
container_name: github_repos_db
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
networks:
|
||||
- webnet
|
||||
env_file: ./config/.env
|
||||
ports:
|
||||
- 5433:5432
|
||||
|
||||
web:
|
||||
image: "github-repos"
|
||||
container_name: github_repos_web
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker/django/Dockerfile
|
||||
# args:
|
||||
# DJANGO_ENV: development
|
||||
# cache_from:
|
||||
# - "github-repos:dev"
|
||||
# - "github-repos:latest"
|
||||
# - "*"
|
||||
# volumes:
|
||||
# - django-static:/var/www/django/static
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- .:/code
|
||||
ports:
|
||||
- 8000:8000
|
||||
depends_on:
|
||||
- db
|
||||
networks:
|
||||
- webnet
|
||||
env_file: ./config/.env
|
||||
command: >
|
||||
bash -c "python manage.py migrate --noinput
|
||||
&& python -Wd manage.py runserver 0.0.0.0:8000"
|
||||
# healthcheck:
|
||||
# # We use `$$` here because:
|
||||
# # one `$` goes to shell,
|
||||
# # one `$` goes to `docker-compose.yml` escaping
|
||||
# test: |
|
||||
# /usr/bin/test $$(
|
||||
# /usr/bin/curl --fail http://localhost:8000/health/?format=json
|
||||
# --write-out "%{http_code}" --silent --output /dev/null
|
||||
# ) -eq 200
|
||||
# interval: 10s
|
||||
# timeout: 5s
|
||||
# retries: 5
|
||||
# start_period: 30s
|
||||
|
||||
# This task is an example of how to extend existing ones:
|
||||
# some_worker:
|
||||
# <<: *web
|
||||
# command: python manage.py worker_process
|
||||
|
||||
networks:
|
||||
# Network for your internals, use it by default:
|
||||
webnet:
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
django-static:
|
40
new-github-repos/docker/caddy/Caddyfile
Normal file
40
new-github-repos/docker/caddy/Caddyfile
Normal file
@ -0,0 +1,40 @@
|
||||
# See https://caddyserver.com/docs
|
||||
|
||||
# Email for Let's Encrypt expiration notices
|
||||
{
|
||||
email {$TLS_EMAIL}
|
||||
}
|
||||
|
||||
# "www" redirect to "non-www" version
|
||||
www.{$DOMAIN_NAME} {
|
||||
redir https://{$DOMAIN_NAME}{uri}
|
||||
}
|
||||
|
||||
{$DOMAIN_NAME} {
|
||||
# HTTPS options:
|
||||
header Strict-Transport-Security max-age=31536000;
|
||||
|
||||
# Removing some headers for improved security:
|
||||
header -Server
|
||||
|
||||
# Exclude matcher for Django assets
|
||||
@excludeDirs {
|
||||
not path /static/* /media/*
|
||||
}
|
||||
|
||||
# Serving dynamic requests:
|
||||
reverse_proxy @excludeDirs web:8000
|
||||
|
||||
# Serves static files, should be the same as `STATIC_ROOT` setting:
|
||||
file_server {
|
||||
root /var/www/django
|
||||
}
|
||||
|
||||
# Allows to use `.gz` files when available:
|
||||
encode gzip
|
||||
|
||||
# Logs:
|
||||
log {
|
||||
output stdout
|
||||
}
|
||||
}
|
96
new-github-repos/docker/ci.sh
Executable file
96
new-github-repos/docker/ci.sh
Executable file
@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
# Initializing global variables and functions:
|
||||
: "${DJANGO_ENV:=development}"
|
||||
|
||||
# Fail CI if `DJANGO_ENV` is not set to `development`:
|
||||
if [ "$DJANGO_ENV" != 'development' ]; then
|
||||
echo 'DJANGO_ENV is not set to development. Running tests is not safe.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pyclean () {
|
||||
# Cleaning cache:
|
||||
find . \
|
||||
| grep -E '(__pycache__|\.hypothesis|\.perm|\.cache|\.static|\.py[cod]$)' \
|
||||
| xargs rm -rf
|
||||
}
|
||||
|
||||
run_ci () {
|
||||
echo '[ci started]'
|
||||
set -x # we want to print commands during the CI process.
|
||||
|
||||
# Testing filesystem and permissions:
|
||||
touch .perm && rm -f .perm
|
||||
touch '/var/www/django/media/.perm' && rm -f '/var/www/django/media/.perm'
|
||||
touch '/var/www/django/static/.perm' && rm -f '/var/www/django/static/.perm'
|
||||
|
||||
# Checking `.env` files:
|
||||
dotenv-linter config/.env config/.env.template
|
||||
|
||||
# Running linting for all python files in the project:
|
||||
flake8 .
|
||||
|
||||
# Running type checking, see https://github.com/typeddjango/django-stubs
|
||||
mypy manage.py server $(find tests -name '*.py')
|
||||
|
||||
# Running tests:
|
||||
pytest --dead-fixtures
|
||||
pytest
|
||||
|
||||
# Run checks to be sure we follow all django's best practices:
|
||||
python manage.py check --fail-level WARNING
|
||||
|
||||
# Run checks to be sure settings are correct (production flag is required):
|
||||
DJANGO_ENV=production python manage.py check --deploy --fail-level WARNING
|
||||
|
||||
# Check that staticfiles app is working fine:
|
||||
DJANGO_ENV=production DJANGO_COLLECTSTATIC_DRYRUN=1 \
|
||||
python manage.py collectstatic --no-input --dry-run
|
||||
|
||||
# Check that all migrations worked fine:
|
||||
python manage.py makemigrations --dry-run --check
|
||||
|
||||
# Check that all migrations are backwards compatible:
|
||||
python manage.py lintmigrations --exclude-apps=axes --warnings-as-errors
|
||||
|
||||
# Checking if all the dependencies are secure and do not have any
|
||||
# known vulnerabilities:
|
||||
safety check --full-report
|
||||
|
||||
# Checking `pyproject.toml` file contents:
|
||||
poetry check
|
||||
|
||||
# Checking dependencies status:
|
||||
pip check
|
||||
|
||||
# Checking docs:
|
||||
doc8 -q docs
|
||||
|
||||
# Checking `yaml` files:
|
||||
yamllint -d '{"extends": "default", "ignore": ".venv"}' -s .
|
||||
|
||||
# Checking translation files, ignoring ordering and locations:
|
||||
polint -i location,unsorted locale
|
||||
|
||||
# Also checking translation files for syntax errors:
|
||||
if find locale -name '*.po' -print0 | grep -q "."; then
|
||||
# Only executes when there is at least one `.po` file:
|
||||
dennis-cmd lint --errorsonly locale
|
||||
fi
|
||||
|
||||
set +x
|
||||
echo '[ci finished]'
|
||||
}
|
||||
|
||||
# Remove any cache before the script:
|
||||
pyclean
|
||||
|
||||
# Clean everything up:
|
||||
trap pyclean EXIT INT TERM
|
||||
|
||||
# Run the CI process:
|
||||
run_ci
|
46
new-github-repos/docker/django/Dockerfile
Normal file
46
new-github-repos/docker/django/Dockerfile
Normal file
@ -0,0 +1,46 @@
|
||||
FROM python:3.8.9-slim-buster
|
||||
|
||||
ENV BUILD_ONLY_PACKAGES='wget' \
|
||||
# python:
|
||||
PYTHONFAULTHANDLER=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONHASHSEED=random \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
# pip:
|
||||
PIP_NO_CACHE_DIR=off \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||
PIP_DEFAULT_TIMEOUT=100 \
|
||||
# poetry:
|
||||
POETRY_VERSION=1.1.4 \
|
||||
POETRY_NO_INTERACTION=1 \
|
||||
POETRY_VIRTUALENVS_CREATE=false \
|
||||
POETRY_CACHE_DIR='/var/cache/pypoetry' \
|
||||
PATH="$PATH:/root/.poetry/bin"
|
||||
|
||||
# System deps:
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
bash \
|
||||
build-essential \
|
||||
curl \
|
||||
gettext \
|
||||
git \
|
||||
libpq-dev \
|
||||
nano \
|
||||
# Defining build-time-only dependencies:
|
||||
$BUILD_ONLY_PACKAGES \
|
||||
&& curl -sSL 'https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py' | python \
|
||||
&& poetry --version \
|
||||
# Removing build-time-only dependencies:
|
||||
&& apt-get remove -y $BUILD_ONLY_PACKAGES \
|
||||
# Cleaning cache:
|
||||
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
|
||||
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -rf $POETRY_CACHE_DIR
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
# Copy only requirements, to cache them in docker layer
|
||||
COPY ./poetry.lock ./pyproject.toml /code/
|
||||
RUN poetry install
|
||||
COPY . /code
|
24
new-github-repos/docker/django/entrypoint.sh
Normal file
24
new-github-repos/docker/django/entrypoint.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
readonly cmd="$*"
|
||||
|
||||
postgres_ready () {
|
||||
# Check that postgres is up and running on port `5432`:
|
||||
dockerize -wait 'tcp://db:5432' -timeout 5s
|
||||
}
|
||||
|
||||
# We need this line to make sure that this container is started
|
||||
# after the one with postgres:
|
||||
until postgres_ready; do
|
||||
>&2 echo 'Postgres is unavailable - sleeping'
|
||||
done
|
||||
|
||||
# It is also possible to wait for other services as well: redis, elastic, mongo
|
||||
>&2 echo 'Postgres is up - continuing...'
|
||||
|
||||
# Evaluating passed command (do not touch):
|
||||
# shellcheck disable=SC2086
|
||||
exec $cmd
|
41
new-github-repos/docker/django/gunicorn.sh
Normal file
41
new-github-repos/docker/django/gunicorn.sh
Normal file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
# We are using `gunicorn` for production, see:
|
||||
# http://docs.gunicorn.org/en/stable/configure.html
|
||||
|
||||
# Check that $DJANGO_ENV is set to "production",
|
||||
# fail otherwise, since it may break things:
|
||||
|
||||
|
||||
echo "DJANGO_ENV is $DJANGO_ENV"
|
||||
if [ "$DJANGO_ENV" != 'production' ]; then
|
||||
echo 'Error: DJANGO_ENV is not set to "production".'
|
||||
echo 'Application will not start.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export DJANGO_ENV
|
||||
|
||||
|
||||
# Run python specific scripts:
|
||||
# Running migrations in startup script might not be the best option, see:
|
||||
# docs/pages/template/production-checklist.rst
|
||||
python /code/manage.py migrate --noinput
|
||||
python /code/manage.py collectstatic --noinput
|
||||
python /code/manage.py compilemessages
|
||||
|
||||
# Start gunicorn:
|
||||
# Docs: http://docs.gunicorn.org/en/stable/settings.html
|
||||
# Concerning `workers` setting see:
|
||||
# https://github.com/wemake-services/wemake-django-template/issues/1022
|
||||
/usr/local/bin/gunicorn server.wsgi \
|
||||
--workers=4 `# Sync worker settings` \
|
||||
--max-requests=2000 \
|
||||
--max-requests-jitter=400 \
|
||||
--bind='0.0.0.0:8000' `# Run Django on 8000 port` \
|
||||
--chdir='/code' `# Locations` \
|
||||
--log-file=- \
|
||||
--worker-tmp-dir='/dev/shm'
|
65
new-github-repos/docker/docker-compose.prod.yml
Normal file
65
new-github-repos/docker/docker-compose.prod.yml
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
|
||||
# This compose-file is production only. So, it should not be called directly.
|
||||
#
|
||||
# Instead, it should be a part of your deploy strategy.
|
||||
# This setup is supposed to be used with `docker-swarm`.
|
||||
# See `./docs/pages/template/production.rst` docs.
|
||||
|
||||
version: "3.6"
|
||||
services:
|
||||
caddy:
|
||||
image: "caddy:2.2.1"
|
||||
restart: unless-stopped
|
||||
env_file: ./config/.env
|
||||
volumes:
|
||||
- ./docker/caddy/Caddyfile:/etc/caddy/Caddyfile # configuration
|
||||
- caddy-config:/config # configuration autosaves
|
||||
- caddy-data:/data # saving certificates
|
||||
- django-static:/var/www/django/static # serving django's statics
|
||||
- django-media:/var/www/django/media # serving django's media
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
depends_on:
|
||||
- web
|
||||
networks:
|
||||
- proxynet
|
||||
|
||||
web:
|
||||
<<: &web
|
||||
# Image for production:
|
||||
image: "registry.gitlab.com/balsh/github-repos:latest"
|
||||
build:
|
||||
target: production_build
|
||||
args:
|
||||
DJANGO_ENV: production
|
||||
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- django-media:/var/www/django/media # since in dev it is app's folder
|
||||
- django-locale:/code/locale # since in dev it is app's folder
|
||||
|
||||
command: sh ./docker/django/gunicorn.sh
|
||||
networks:
|
||||
- proxynet
|
||||
expose:
|
||||
- 8000
|
||||
|
||||
# This task is an example of how to extend existing ones:
|
||||
# some_wroker:
|
||||
# <<: *web
|
||||
# command: python manage.py worker_process
|
||||
# deploy:
|
||||
# replicas: 2
|
||||
|
||||
networks:
|
||||
# Network for your proxy server and application to connect them,
|
||||
# do not use it for anything else!
|
||||
proxynet:
|
||||
|
||||
volumes:
|
||||
django-media:
|
||||
django-locale:
|
||||
caddy-config:
|
||||
caddy-data:
|
20
new-github-repos/docs/Makefile
Normal file
20
new-github-repos/docs/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = python -msphinx
|
||||
SPHINXPROJ = wemake-django-template
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
18
new-github-repos/docs/README.md
Normal file
18
new-github-repos/docs/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Starting with the docs
|
||||
|
||||
We are using [Sphinx](http://www.sphinx-doc.org) to manage our documentation.
|
||||
If you have never worked with `Sphinx` this guide
|
||||
will cover the most common uses cases.
|
||||
|
||||
|
||||
## Quickstart
|
||||
|
||||
1. Clone this repository
|
||||
2. Install dependencies, [here's how to do it](pages/template/development.rst)
|
||||
3. Run `cd docs && make html`
|
||||
4. Open `_build/html/index.html` with your browser
|
||||
|
||||
|
||||
## Where to go next
|
||||
|
||||
Read the main page of the opened documentation website. It will guide you.
|
0
new-github-repos/docs/_static/.gitkeep
vendored
Normal file
0
new-github-repos/docs/_static/.gitkeep
vendored
Normal file
28
new-github-repos/docs/_templates/moreinfo.html
vendored
Normal file
28
new-github-repos/docs/_templates/moreinfo.html
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<h3>
|
||||
Links
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/wemake-services/wemake-django-template">
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://wemake.services/meta">
|
||||
Meta
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://wemake.services/">
|
||||
wemake.services
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<a aria-label="Star wemake-services/wemake-django-template on GitHub" data-count-aria-label="# stargazers on GitHub" data-count-api="/repos/wemake-services/wemake-django-template#stargazers_count" data-count-href="wemake-services/wemake-django-template/stargazers" data-style="mega" data-icon="octicon-star" href="https://github.com/wemake-services/wemake-django-template" class="github-button">Star</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
136
new-github-repos/docs/conf.py
Normal file
136
new-github-repos/docs/conf.py
Normal file
@ -0,0 +1,136 @@
|
||||
# wemake-django-template documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sat Sep 30 12:42:34 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
|
||||
import os
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
|
||||
import tomlkit
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
# We need this block, because
|
||||
# django might be not installed, maybe we are running our
|
||||
# build process in ReadTheDocs?
|
||||
# https://github.com/wemake-services/wemake-django-template/issues/133
|
||||
with suppress(ImportError):
|
||||
import django # noqa: WPS433
|
||||
|
||||
# Normal django setup. That's how it should be in development:
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'server.settings'
|
||||
django.setup()
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
def _get_project_meta():
|
||||
with open('../pyproject.toml') as pyproject:
|
||||
file_contents = pyproject.read()
|
||||
|
||||
return tomlkit.parse(file_contents)['tool']['poetry']
|
||||
|
||||
|
||||
pkg_meta = _get_project_meta()
|
||||
project = str(pkg_meta['name'])
|
||||
author = str(pkg_meta['authors'][0])
|
||||
copyright = author # noqa: WPS125
|
||||
|
||||
# The short X.Y version
|
||||
version = str(pkg_meta['version'])
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = version
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = '3.3'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.githubpages',
|
||||
'sphinx.ext.napoleon',
|
||||
|
||||
# 3rd party, order matters:
|
||||
# https://github.com/wemake-services/wemake-django-template/issues/159
|
||||
'sphinx_autodoc_typehints',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
source_suffix = ['.rst']
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'about.html',
|
||||
'navigation.html',
|
||||
'moreinfo.html',
|
||||
'searchbox.html',
|
||||
],
|
||||
}
|
0
new-github-repos/docs/documents/.gitkeep
Normal file
0
new-github-repos/docs/documents/.gitkeep
Normal file
102
new-github-repos/docs/index.rst
Normal file
102
new-github-repos/docs/index.rst
Normal file
@ -0,0 +1,102 @@
|
||||
Welcome to wemake-django-template's documentation!
|
||||
==================================================
|
||||
|
||||
|
||||
What this project is all about?
|
||||
The main idea of this project is to provide a fully configured
|
||||
template for ``django`` projects, where code quality, testing,
|
||||
documentation, security, and scalability are number one priorities.
|
||||
|
||||
This template is a result of implementing
|
||||
`our processes <https://github.com/wemake-services/meta>`_,
|
||||
it should not be considered as an independent part.
|
||||
|
||||
|
||||
Goals
|
||||
-----
|
||||
|
||||
When developing this template we had several goals in mind:
|
||||
|
||||
- Development environment should be bootstrapped easily,
|
||||
so we use ``docker-compose`` for that
|
||||
- Development should be consistent, so we use strict quality and style checks
|
||||
- Development, testing, and production should have the same environment,
|
||||
so again we develop, test, and run our apps in ``docker`` containers
|
||||
- Documentation and codebase are the only sources of truth
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
This project implies that:
|
||||
|
||||
- You are using ``docker`` for deployment
|
||||
- You are using Gitlab and Gitlab CI
|
||||
- You are not using any frontend assets in ``django``,
|
||||
you store your frontend separately
|
||||
|
||||
|
||||
Should I choose this template?
|
||||
------------------------------
|
||||
|
||||
This template is oriented on big projects,
|
||||
when there are multiple people working on it for a long period of time.
|
||||
|
||||
If you want to simply create a working prototype without all these
|
||||
limitations and workflows - feel free to choose any
|
||||
`other template <https://github.com/audreyr/cookiecutter#python-django>`_.
|
||||
|
||||
|
||||
How to start
|
||||
------------
|
||||
|
||||
You should start with reading the documentation.
|
||||
Reading order is important.
|
||||
|
||||
There are multiple processes that you need to get familiar with:
|
||||
|
||||
- First time setup phase: what system requirements you must fulfill,
|
||||
how to install dependencies, how to start your project
|
||||
- Active development phase: how to make changes, run tests,
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Setting things up:
|
||||
|
||||
pages/template/overview.rst
|
||||
pages/template/development.rst
|
||||
pages/template/django.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Quality assurance:
|
||||
|
||||
pages/template/documentation.rst
|
||||
pages/template/linters.rst
|
||||
pages/template/testing.rst
|
||||
pages/template/security.rst
|
||||
pages/template/gitlab-ci.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Production:
|
||||
|
||||
pages/template/production-checklist.rst
|
||||
pages/template/production.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Extras:
|
||||
|
||||
pages/template/upgrading-template.rst
|
||||
pages/template/faq.rst
|
||||
pages/template/troubleshooting.rst
|
||||
|
||||
|
||||
Indexes and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
36
new-github-repos/docs/make.bat
Normal file
36
new-github-repos/docs/make.bat
Normal file
@ -0,0 +1,36 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=wemake-django-template
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
0
new-github-repos/docs/pages/project/.gitkeep
Normal file
0
new-github-repos/docs/pages/project/.gitkeep
Normal file
178
new-github-repos/docs/pages/template/development.rst
Normal file
178
new-github-repos/docs/pages/template/development.rst
Normal file
@ -0,0 +1,178 @@
|
||||
Development
|
||||
===========
|
||||
|
||||
Our development process is focused on high quality and development comfort.
|
||||
We use tools that are proven to be the best in class.
|
||||
|
||||
There are two possible ways to develop your apps.
|
||||
|
||||
1. local development
|
||||
2. development inside ``docker``
|
||||
|
||||
You can choose one or use both at the same time.
|
||||
How to choose what method should you use?
|
||||
|
||||
Local development is much easier and much faster.
|
||||
You can choose it if you don't have too many infrastructure dependencies.
|
||||
That's a default option for the new projects.
|
||||
|
||||
Choosing ``docker`` development means that you already have a complex
|
||||
setup of different technologies, containers, networks, etc.
|
||||
This is a default option for older and more complicated projects.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
We use ``poetry`` to manage dependencies.
|
||||
So, please do not use ``virtualenv`` or ``pip`` directly.
|
||||
Before going any further, please,
|
||||
take a moment to read the `official documentation <https://poetry.eustace.io/>`_
|
||||
about ``poetry`` to know some basics.
|
||||
|
||||
If you are using ``docker`` then prepend ``docker-compose run --rm web``
|
||||
before any of those commands to execute them.
|
||||
|
||||
Please, note that you don't need almost all of them with ``docker``.
|
||||
You can just skip this sub-section completely.
|
||||
Go right to `Development with docker`_.
|
||||
|
||||
Installing dependencies
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You do not need to run any of these command for ``docker`` based development,
|
||||
since it is already executed inside ``Dockerfile``.
|
||||
|
||||
Please, note that ``poetry`` will automatically create a ``virtualenv`` for
|
||||
this project. It will use you current ``python`` version.
|
||||
To install all existing dependencies run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
poetry install
|
||||
|
||||
To install dependencies for production use, you will need to run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
poetry install --no-dev
|
||||
|
||||
And to activate ``virtualenv`` created by ``poetry`` run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
poetry shell
|
||||
|
||||
Adding new dependencies
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To add a new dependency you can run:
|
||||
|
||||
- ``poetry add django`` to install ``django`` as a production dependency
|
||||
- ``poetry add --dev pytest`` to install ``pytest``
|
||||
as a development dependency
|
||||
|
||||
This command might be used with ``docker``.
|
||||
|
||||
Updating poetry version
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Package managers should also be pinned very strictly.
|
||||
We had a lot of problems in production
|
||||
because we were not pinning package manager versions.
|
||||
|
||||
This can result in broken ``lock`` files, inconsistent installation process,
|
||||
bizarre bugs, and missing packages. You do not want to experience that!
|
||||
|
||||
How can we have the same ``poetry`` version for all users in a project?
|
||||
That's where ``[build-system]`` tag shines. It specifies the exact version of
|
||||
your ``poetry`` installation that must be used for the project.
|
||||
Version mismatch will fail your build.
|
||||
|
||||
When you want to update ``poetry``, you have to bump it in several places:
|
||||
|
||||
1. ``pyproject.toml``
|
||||
2. ``docker/django/Dockerfile``
|
||||
|
||||
Then you are fine!
|
||||
|
||||
|
||||
Development with docker
|
||||
-----------------------
|
||||
|
||||
To start development server inside ``docker`` you will need to run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker-compose build
|
||||
docker-compose run --rm web python manage.py migrate
|
||||
docker-compose up
|
||||
|
||||
Running scripts inside docker
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As we have already mentioned inside the previous section
|
||||
we use ``docker-compose run`` to run scripts inside docker.
|
||||
|
||||
What do you need to know about it?
|
||||
|
||||
1. You can run anything you want: ``poetry``, ``python``, ``sh``, etc
|
||||
2. Most likely it will have a permanent effect, due to ``docker volumes``
|
||||
3. You need to use ``--rm`` to automatically remove this container afterward
|
||||
|
||||
**Note**: ``docker`` commands do not need to use ``virtualenv`` at all.
|
||||
|
||||
Extra configuration
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You might want to tweak ``INTERNAL_IPS`` ``django`` setting
|
||||
to include your ``docker`` container address into it.
|
||||
Otherwise ``django-debug-toolbar`` might not show up.
|
||||
|
||||
To get your ``docker`` ip run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker inspect your-container-name | grep -e '"Gateway"'
|
||||
|
||||
You can also configure a permanent hostname inside your ``/etc/hosts`` to
|
||||
access your ``docker`` containers with a permanent hostname.
|
||||
|
||||
|
||||
Local development
|
||||
-----------------
|
||||
|
||||
When cloning a project for the first time you may
|
||||
need to configure it properly,
|
||||
see :ref:`django` section for more information.
|
||||
|
||||
**Note**, that you will need to activate ``virtualenv`` created
|
||||
by ``poetry`` before running any of these commands.
|
||||
**Note**, that you only need to run these commands once per project.
|
||||
|
||||
Local database
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
When using local development environment without ``docker``,
|
||||
you will need a ``postgres`` up and running.
|
||||
To create new development database run
|
||||
(make sure that database and user names are correct for your case):
|
||||
|
||||
.. code:: bash
|
||||
|
||||
psql postgres -U postgres -f sql/create_database.sql
|
||||
|
||||
Then migrate your database:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
Running project
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
If you have reached this point, you should be able to run the project.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python manage.py runserver
|
167
new-github-repos/docs/pages/template/django.rst
Normal file
167
new-github-repos/docs/pages/template/django.rst
Normal file
@ -0,0 +1,167 @@
|
||||
.. _django:
|
||||
|
||||
Django
|
||||
======
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
We share the same configuration structure for almost every possible
|
||||
environment.
|
||||
|
||||
We use:
|
||||
|
||||
- ``django-split-settings`` to organize ``django``
|
||||
settings into multiple files and directories
|
||||
- ``.env`` files to store secret configuration
|
||||
- ``python-decouple`` to load ``.env`` files into ``django``
|
||||
|
||||
Components
|
||||
~~~~~~~~~~
|
||||
|
||||
If you have some specific components like ``celery`` or ``mailgun`` installed,
|
||||
they could be configured in separate files.
|
||||
Just create a new file in ``server/settings/components/``.
|
||||
Then add it into ``server/settings/__init__.py``.
|
||||
|
||||
Environments
|
||||
~~~~~~~~~~~~
|
||||
|
||||
To run ``django`` on different environments just
|
||||
specify ``DJANGO_ENV`` environment variable.
|
||||
It must have the same name as one of the files
|
||||
from ``server/settings/environments/``.
|
||||
Then, values from this file will override other settings.
|
||||
|
||||
Local settings
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
If you need some specific local configuration tweaks,
|
||||
you can create file ``server/settings/environments/local.py.template``
|
||||
to ``server/settings/environments/local.py``.
|
||||
It will be loaded into your settings automatically if exists.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
cp server/settings/environments/local.py.template server/settings/environments/local.py
|
||||
|
||||
See ``local.py.template`` version for the reference.
|
||||
|
||||
|
||||
Secret settings
|
||||
---------------
|
||||
|
||||
We share the same mechanism for secret settings for all our tools.
|
||||
We use ``.env`` files for ``django``, ``postgres``, ``docker``, etc.
|
||||
|
||||
Initially, you will need to copy file
|
||||
``config/.env.template`` to ``config/.env``:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
cp config/.env.template config/.env
|
||||
|
||||
When adding any new secret ``django`` settings you will need to:
|
||||
|
||||
1. Add new key and value to ``config/.env``
|
||||
2. Add new key without value to ``config/.env.template``,
|
||||
add a comment on how to get this value for other users
|
||||
3. Add new variable inside ``django`` settings
|
||||
4. Use ``python-decouple`` to load this ``env`` variable like so:
|
||||
``MY_SECRET = config('MY_SECRET')``
|
||||
|
||||
|
||||
Secret settings in production
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We do not store our secret settings inside our source code.
|
||||
All sensible settings are stored in ``config/.env`` file,
|
||||
which is not tracked by the version control.
|
||||
|
||||
So, how do we store secrets? We store them as secret environment variables
|
||||
in `GitLab CI <https://docs.gitlab.com/ce/ci/variables/README.html#secret-variables>`_.
|
||||
Then we use `dump-env <https://github.com/sobolevn/dump-env>`_
|
||||
to dump variables from both environment and ``.env`` file template.
|
||||
Then, this file is copied inside ``docker`` image and when
|
||||
this image is built - everything is ready for production.
|
||||
|
||||
Here's an example:
|
||||
|
||||
1. We add a ``SECRET_DJANGO_SECRET_KEY`` variable to Gitlab CI secret variables
|
||||
2. Then ``dump-env`` dumps ``SECRET_DJANGO_SECRET_KEY``
|
||||
as ``DJANGO_SECRET_KEY`` and writes it to ``config/.env`` file
|
||||
3. Then it is loaded by ``django`` inside the settings:
|
||||
``SECRET_KEY = config('DJANGO_SECRET_KEY')``
|
||||
|
||||
However, there are different options to store secret settings:
|
||||
|
||||
- `ansible-vault <https://docs.ansible.com/ansible/2.4/vault.html>`_
|
||||
- `git-secret <https://github.com/sobolevn/git-secret>`_
|
||||
- `Vault <https://www.vaultproject.io/>`_
|
||||
|
||||
Depending on a project we use different tools.
|
||||
With ``dump-env`` being the default and the simplest one.
|
||||
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
We use different ``django`` extensions that make your life easier.
|
||||
Here's a full list of the extensions for both development and production:
|
||||
|
||||
- `django-split-settings`_ - organize
|
||||
``django`` settings into multiple files and directories.
|
||||
Easily override and modify settings.
|
||||
Use wildcards in settings file paths and mark settings files as optional
|
||||
- `django-axes`_ - keep track
|
||||
of failed login attempts in ``django`` powered sites
|
||||
- `django-csp`_ - `Content Security Policy`_ for ``django``
|
||||
- `django-referrer-policy`_ - middleware implementing the `Referrer-Policy`_
|
||||
- `django-health-check`_ - checks for various conditions and provides reports
|
||||
when anomalous behavior is detected
|
||||
- `django-add-default-value`_ - this django Migration Operation can be used to
|
||||
transfer a Fields default value to the database scheme
|
||||
- `django-deprecate-fields`_ - this package allows deprecating model fields and
|
||||
allows removing them in a backwards compatible manner
|
||||
- `django-migration-linter`_ - detect backward incompatible migrations for
|
||||
your django project
|
||||
- `zero-downtime-migrations`_ - apply ``django`` migrations on PostgreSql
|
||||
without long locks on tables
|
||||
|
||||
Development only extensions:
|
||||
|
||||
- `django-debug-toolbar`_ - a configurable set of panels that
|
||||
display various debug information about the current request/response
|
||||
- `django-querycount`_ - middleware that prints the number
|
||||
of DB queries to the runserver console
|
||||
- `nplusone`_ - auto-detecting the `n+1 queries problem`_ in ``django``
|
||||
|
||||
.. _django-split-settings: https://github.com/sobolevn/django-split-settings
|
||||
.. _django-axes: https://github.com/jazzband/django-axes
|
||||
.. _django-csp: https://github.com/mozilla/django-csp
|
||||
.. _`Content Security Policy`: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||
.. _django-referrer-policy: https://github.com/ubernostrum/django-referrer-policy
|
||||
.. _`Referrer-Policy`: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
|
||||
.. _django-health-check: https://github.com/KristianOellegaard/django-health-check
|
||||
.. _django-add-default-value: https://github.com/3YOURMIND/django-add-default-value
|
||||
.. _django-deprecate-fields: https://github.com/3YOURMIND/django-deprecate-fields
|
||||
.. _django-migration-linter: https://github.com/3YOURMIND/django-migration-linter
|
||||
.. _zero-downtime-migrations: https://github.com/yandex/zero-downtime-migrations
|
||||
.. _django-debug-toolbar: https://github.com/jazzband/django-debug-toolbar
|
||||
.. _django-querycount: https://github.com/bradmontgomery/django-querycount
|
||||
.. _nplusone: https://github.com/jmcarp/nplusone
|
||||
.. _`n+1 queries problem`: https://stackoverflow.com/questions/97197/what-is-the-n1-select-query-issue
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
- `django-split-settings tutorial <https://medium.com/wemake-services/managing-djangos-settings-e2b7f496120d>`_
|
||||
- `docker env-file docs <https://docs.docker.com/compose/env-file/>`_
|
||||
|
||||
|
||||
Django admin
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- `Django Admin Cookbook <https://books.agiliq.com/projects/django-admin-cookbook/en/latest/>`_
|
98
new-github-repos/docs/pages/template/documentation.rst
Normal file
98
new-github-repos/docs/pages/template/documentation.rst
Normal file
@ -0,0 +1,98 @@
|
||||
Documentation
|
||||
=============
|
||||
|
||||
`We <https://github.com/wemake-services/meta>`_ write a lot of documentation.
|
||||
Since we believe, that documentation is a crucial factor
|
||||
which defines project success or failure.
|
||||
|
||||
Here's how we write docs for ``django`` projects.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
We are using ``sphinx`` as a documentation builder.
|
||||
We use ``sphinx.ext.napoleon`` to write
|
||||
pretty docstrings inside the source code.
|
||||
We also use ``sphinx_autodoc_typehints`` to inject type annotations into docs.
|
||||
|
||||
We also use two sources of truth for the dependencies here:
|
||||
|
||||
- ``docs/requirements.txt``
|
||||
- ``pyproject.toml``
|
||||
|
||||
Why? Because we are using ReadTheDocs
|
||||
for this template (only for original Github repo), and it
|
||||
does only support traditional ``requirements.txt``.
|
||||
|
||||
|
||||
Structure
|
||||
---------
|
||||
|
||||
We use a clear structure for this documentation.
|
||||
|
||||
- ``pages/template`` contains docs
|
||||
from `wemake-django-template <https://github.com/wemake-services/wemake-django-template>`_.
|
||||
These files should not be modified locally.
|
||||
If you have any kind of question or problems,
|
||||
just open an issue `on github <https://github.com/wemake-services/wemake-django-template/issues>`_
|
||||
- ``pages/project`` contains everything related to the project itself.
|
||||
Usage examples, an auto-generated documentation from your source code,
|
||||
configuration, business, and project goals
|
||||
- ``documents`` contains different non-sphinx documents
|
||||
like ``doc`` files, spreadsheets, and mockups
|
||||
|
||||
Please, do not mix it up.
|
||||
|
||||
How to structure project docs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It is a good practice to write a single ``rst`` document
|
||||
for every single ``py`` file.
|
||||
Obviously, ``rst`` structure fully copies the structure of your source code.
|
||||
This way it is very easy to navigate through the docs,
|
||||
since you already know the structure.
|
||||
|
||||
For each ``django`` application we tend to create
|
||||
a file called ``index.rst`` which is considered
|
||||
the main file for the application.
|
||||
|
||||
And ``pages/project/index.rst`` is the main file for the whole project.
|
||||
|
||||
|
||||
How to contribute
|
||||
-----------------
|
||||
|
||||
We enforce everyone to write clean and explaining documentation.
|
||||
However, there are several rules about writing styling.
|
||||
|
||||
We are using `doc8 <https://pypi.python.org/pypi/doc8>`_ to validate our docs.
|
||||
So, here's the command to do it:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
doc8 ./docs
|
||||
|
||||
This is also used in our CI process, so your build will fail
|
||||
if there are violations.
|
||||
|
||||
|
||||
Useful plugins
|
||||
--------------
|
||||
|
||||
Some ``sphinx`` plugins are not included, since they are very specific.
|
||||
However, they are very useful:
|
||||
|
||||
- `sphinxcontrib-mermaid <https://github.com/mgaitan/sphinxcontrib-mermaid>`_ - sphinx plugin to create general flowcharts, sequence and gantt diagrams
|
||||
- `sphinxcontrib-plantuml <https://github.com/sphinx-contrib/plantuml/>`_ - sphinx plugin to create UML diagrams
|
||||
- `nbsphinx <https://github.com/spatialaudio/nbsphinx>`_ - sphinx plugin to embed ``ipython`` notebooks into your docs
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
- `sphinx <http://www.sphinx-doc.org/en/stable/>`_
|
||||
- `sphinx with django <https://docs.djangoproject.com/en/2.2/internals/contributing/writing-documentation/#getting-started-with-sphinx>`_
|
||||
- `sphinx-autodoc-typehints <https://github.com/agronholm/sphinx-autodoc-typehints>`_
|
||||
- `Architecture Decision Record (ADR) <https://github.com/joelparkerhenderson/architecture_decision_record>`_
|
||||
- `adr-tools <https://github.com/npryce/adr-tools>`_
|
29
new-github-repos/docs/pages/template/faq.rst
Normal file
29
new-github-repos/docs/pages/template/faq.rst
Normal file
@ -0,0 +1,29 @@
|
||||
Frequently asked questions
|
||||
==========================
|
||||
|
||||
Will you ever support drf / celery / flask / gevent?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
No. This template is focused on bringing best practices to ``django``
|
||||
projects. It only includes workflow and configuration for this framework.
|
||||
|
||||
Other tools are not mandatory. And can easily be added by a developer.
|
||||
|
||||
Will you have an build-time option to include or change anything?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
No, we believe that options bring inconsistencies to the project.
|
||||
You can also make the wrong choice. So, we are protecting you from that.
|
||||
|
||||
You can only have options that are already present in this template.
|
||||
Fork it, if you do not agree with this policy.
|
||||
|
||||
This code quality is unbearable! Can I turn it off?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Of course, no one can stop you from that.
|
||||
But what the point in using this template then?
|
||||
|
||||
Our code quality defined by this template is minimally acceptable.
|
||||
We know tools to make it even better. But they are not included.
|
||||
Since they are literally hardcore.
|
56
new-github-repos/docs/pages/template/gitlab-ci.rst
Normal file
56
new-github-repos/docs/pages/template/gitlab-ci.rst
Normal file
@ -0,0 +1,56 @@
|
||||
Gitlab CI
|
||||
=========
|
||||
|
||||
We use ``Gitlab CI`` to build our containers, test it,
|
||||
and store them in the internal registry.
|
||||
|
||||
These images are then pulled into the production servers.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
All configuration is done inside ``.gitlab-ci.yml``.
|
||||
|
||||
|
||||
Pipelines
|
||||
---------
|
||||
|
||||
We have two pipelines configured: for ``master`` and other branches.
|
||||
That's how it works: we only run testing for feature branches and do the whole
|
||||
building/testing/deploying process for the ``master`` branch.
|
||||
|
||||
This allows us to speed up development process.
|
||||
|
||||
|
||||
Automatic dependencies update
|
||||
-----------------------------
|
||||
|
||||
You can use `dependabot <https://github.com/dependabot/dependabot-script>`_
|
||||
to enable automatic dependencies updates via Pull Requests to your repository.
|
||||
Similar to the original template repository: `list of pull requests <https://github.com/wemake-services/wemake-django-template/pulls?q=is%3Apr+author%3Aapp%2Fdependabot>`_.
|
||||
|
||||
It is available to both Github and Gitlab.
|
||||
But, for Gitlab version you currently have to update your `.gitlab-ci.yml <https://github.com/dependabot/dependabot-script/blob/master/.gitlab-ci.example.yml>`_.
|
||||
|
||||
|
||||
Secret variables
|
||||
----------------
|
||||
|
||||
If some real secret variables are required, then you can use `gitlab secrets <https://docs.gitlab.com/ee/ci/variables/#secret-variables>`_.
|
||||
And these kind of variables are required *most* of the time.
|
||||
|
||||
See :ref:`django` on how to use ``dump-env`` and ``gitlab-ci`` together.
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
After each deploy from master branch this documentation compiles into nice looking html page.
|
||||
See `gitlab pages info <https://docs.gitlab.com/ee/user/project/pages/>`_.
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
- `Container Registry <https://gitlab.com/help/user/project/container_registry>`_
|
||||
- `Gitlab CI/CD <https://about.gitlab.com/features/gitlab-ci-cd/>`_
|
130
new-github-repos/docs/pages/template/linters.rst
Normal file
130
new-github-repos/docs/pages/template/linters.rst
Normal file
@ -0,0 +1,130 @@
|
||||
.. _linters:
|
||||
|
||||
Linters
|
||||
=======
|
||||
|
||||
This project uses several linters to make coding style consistent.
|
||||
All configuration is stored inside ``setup.cfg``.
|
||||
|
||||
|
||||
wemake-python-styleguide
|
||||
------------------------
|
||||
|
||||
``wemake-python-styleguide`` is a ``flake8`` based plugin.
|
||||
And it is also the strictest and most opinionated python linter ever.
|
||||
See `wemake-python-styleguide <https://wemake-python-styleguide.readthedocs.io/en/latest/>`_
|
||||
docs.
|
||||
|
||||
Things that are included in the linting process:
|
||||
|
||||
- `flake8 <http://flake8.pycqa.org/>`_ is used a general tool for linting
|
||||
- `isort <https://github.com/timothycrosley/isort>`_ is used to validate ``import`` order
|
||||
- `bandit <https://github.com/PyCQA/bandit>`_ for static security checks
|
||||
- `eradicate <https://github.com/myint/eradicate>`_ to find dead code
|
||||
- and more!
|
||||
|
||||
Running linting process for all ``python`` files in the project:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
flake8 .
|
||||
|
||||
Extra plugins
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
We also use some extra plugins for ``flake8``
|
||||
that are not bundled with ``wemake-python-styleguide``:
|
||||
|
||||
- `flake8-pytest <https://github.com/vikingco/flake8-pytest>`_ - ensures that ``pytest`` best practices are used
|
||||
- `flake8-pytest-style <https://github.com/m-burst/flake8-pytest-style>`_ - ensures that ``pytest`` tests and fixtures are written in a single style
|
||||
- `flake8-django <https://github.com/rocioar/flake8-django>`_ - plugin to enforce best practices in a ``django`` project
|
||||
|
||||
|
||||
django-migration-linter
|
||||
-----------------------
|
||||
|
||||
We use ``django-migration-linter`` to find backward incompatible migrations.
|
||||
It allows us to write 0-downtime friendly code.
|
||||
|
||||
See `django-migration-linter <https://github.com/3YOURMIND/django-migration-linter>`_
|
||||
docs, it contains a lot of useful information about ways and tools to do it.
|
||||
|
||||
That's how this check is executed:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python manage.py lintmigrations --exclude-apps=axes
|
||||
|
||||
Important note: you might want to exclude some packages with broken migrations.
|
||||
Sometimes, there's nothing we can do about it.
|
||||
|
||||
|
||||
yamllint
|
||||
--------
|
||||
|
||||
Is used to lint your ``yaml`` files.
|
||||
See `yamllint <https://github.com/adrienverge/yamllint>`_ docs.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
yamllint -d '{"extends": "default", "ignore": ".venv"}' -s .
|
||||
|
||||
|
||||
dotenv-linter
|
||||
-------------
|
||||
|
||||
Is used to lint your ``.env`` files.
|
||||
See `dotenv-linter <https://github.com/wemake-services/dotenv-linter>`_ docs.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
dotenv-linter config/.env config/.env.template
|
||||
|
||||
|
||||
polint and dennis
|
||||
-----------------
|
||||
|
||||
Are used to lint your ``.po`` files.
|
||||
See `polint <https://github.com/ziima/polint>`_ docs.
|
||||
Also see `dennis <https://dennis.readthedocs.io/en/latest/linting.html>`_ docs.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
polint -i location,unsorted locale
|
||||
dennis-cmd lint --errorsonly locale
|
||||
|
||||
|
||||
Packaging
|
||||
---------
|
||||
|
||||
We also use ``pip`` and ``poetry`` self checks to be sure
|
||||
that packaging works correctly.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
poetry check && pip check
|
||||
|
||||
|
||||
Linters that are not included
|
||||
-----------------------------
|
||||
|
||||
Sometimes we use several other linters that are not included.
|
||||
That's because they require another technology stack to be installed
|
||||
or just out of scope.
|
||||
|
||||
We also recommend to check the list of linters
|
||||
`recommended by wemake-python-styleguide <https://wemake-python-stylegui.de/en/latest/pages/usage/integrations/extras.html>`_.
|
||||
|
||||
Here's the list of these linters. You may still find them useful.
|
||||
|
||||
shellcheck
|
||||
~~~~~~~~~~
|
||||
|
||||
This linter is used to lint your ``.sh`` files.
|
||||
See `shellcheck <https://www.shellcheck.net/>`_ docs.
|
||||
|
||||
hadolint
|
||||
~~~~~~~~
|
||||
|
||||
This linter is used to lint your ``Dockerfile`` syntax.
|
||||
See `hadolint <https://github.com/hadolint/hadolint>`_
|
135
new-github-repos/docs/pages/template/overview.rst
Normal file
135
new-github-repos/docs/pages/template/overview.rst
Normal file
@ -0,0 +1,135 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
|
||||
System requirements
|
||||
-------------------
|
||||
|
||||
- ``git`` with a version at least ``2.16`` or higher
|
||||
- ``docker`` with a version at least ``18.02`` or higher
|
||||
- ``docker-compose`` with a version at least ``1.21`` or higher
|
||||
- ``python`` with exact version, see ``pyproject.toml``
|
||||
|
||||
|
||||
Architecture
|
||||
------------
|
||||
|
||||
config
|
||||
~~~~~~
|
||||
|
||||
- ``config/.env.template`` - a basic example of what keys must be contained in
|
||||
your ``.env`` file, this file is committed to VCS
|
||||
and must not contain private or secret values
|
||||
- ``config/.env`` - main file for secret configuration,
|
||||
contains private and secret values, should not be committed to VCS
|
||||
|
||||
root project
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- ``README.md`` - main readme file, it specifies the entry
|
||||
point to the project's documentation
|
||||
- ``.dockerignore`` - specifies what files should not be
|
||||
copied to the ``docker`` image
|
||||
- ``.editorconfig`` - file with format specification.
|
||||
You need to install the required plugin for your IDE in order to enable it
|
||||
- ``.gitignore`` - file that specifies
|
||||
what should we commit into the repository and we should not
|
||||
- ``.gitlab-ci.yml`` - GitLab CI configuration file.
|
||||
It basically defines what to do with your project
|
||||
after pushing it to the repository. Currently it is used for testing
|
||||
and releasing a ``docker`` image
|
||||
- ``docker-compose.yml`` - this the file specifies ``docker`` services
|
||||
that are needed for development and testing
|
||||
- ``docker-compose.override.yml`` - local override for ``docker-compose``.
|
||||
Is applied automatically and implicitly when
|
||||
no arguments provided to ``docker-compose`` command
|
||||
- ``manage.py`` - main file for your ``django`` project.
|
||||
Used as an entry point for the ``django`` project
|
||||
- ``pyproject.toml`` - main file of the project.
|
||||
It defines the project's dependencies.
|
||||
- ``poetry.lock`` - lock file for dependencies.
|
||||
It is used to install exactly the same versions of dependencies on each build
|
||||
- ``setup.cfg`` - configuration file, that is used by all tools in this project
|
||||
- ``locale/`` - helper folder, that is used to store locale data,
|
||||
empty by default
|
||||
- ``sql/`` - helper folder, that contains ``sql`` script for database setup
|
||||
and teardown for local development
|
||||
|
||||
server
|
||||
~~~~~~
|
||||
|
||||
- ``server/__init__.py`` - package definition, empty file
|
||||
- ``server/urls.py`` - ``django`` `urls definition <https://docs.djangoproject.com/en/2.2/topics/http/urls/>`_
|
||||
- ``server/wsgi.py`` - ``django`` `wsgi definition <https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface>`_
|
||||
- ``server/apps/`` - place to put all your apps into
|
||||
- ``server/apps/main`` - ``django`` application, used as an example,
|
||||
could be removed
|
||||
- ``server/settings`` - settings defined with ``django-split-settings``,
|
||||
see this `tutorial <https://medium.com/wemake-services/managing-djangos-settings-e2b7f496120d>`_
|
||||
for more information
|
||||
- ``server/templates`` - external folder for ``django`` templates,
|
||||
used for simple files as ``robots.txt`` and so on
|
||||
|
||||
docker
|
||||
~~~~~~
|
||||
|
||||
- ``docker/ci.sh`` - file that specifies all possible checks that
|
||||
we execute during our CI process
|
||||
- ``docker/docker-compose.prod.yml`` - additional service definition file
|
||||
used for production
|
||||
- ``docker/django/Dockerfile`` - ``django`` container definition,
|
||||
used both for development and production
|
||||
- ``docker/django/entrypoint.sh`` - entry point script that is used
|
||||
when ``django`` container is starting
|
||||
- ``docker/django/gunicorn.sh`` - production script for ``django``,
|
||||
that's how we configure ``gunicorn`` runner
|
||||
- ``docker/caddy/Caddyfile`` - configuration file for Caddy webserver
|
||||
|
||||
tests
|
||||
~~~~~
|
||||
|
||||
- ``tests/test_server`` - tests that ensures that basic ``django``
|
||||
stuff is working, should not be removed
|
||||
- ``tests/test_apps/test_main`` - example tests for the ``django`` app,
|
||||
could be removed
|
||||
- ``tests/conftest.py`` - main configuration file for ``pytest`` runner
|
||||
|
||||
docs
|
||||
~~~~
|
||||
|
||||
- ``docs/Makefile`` - command file that builds the documentation for Unix
|
||||
- ``docs/make.bat`` - command file for Windows
|
||||
- ``docs/conf.py`` - ``sphinx`` configuration file
|
||||
- ``docs/index.rst`` - main documentation file, used as an entry point
|
||||
- ``docs/pages/project`` - folder that will contain
|
||||
documentation written by you!
|
||||
- ``docs/pages/template`` - folder that contains documentation that
|
||||
is common for each project built with this template
|
||||
- ``docs/documents`` - folder that should contain any documents you have:
|
||||
spreadsheets, images, requirements, presentations, etc
|
||||
- ``docs/requirements.txt`` - helper file, contains dependencies
|
||||
for ``readthedocs`` service. Can be removed
|
||||
- ``docs/README.rst`` - helper file for this directory,
|
||||
just tells what to do next
|
||||
|
||||
|
||||
Container internals
|
||||
-------------------
|
||||
|
||||
We use the ``docker-compose`` to link different containers together.
|
||||
We also utilize different ``docker`` networks to control access.
|
||||
|
||||
Some containers might have long starting times, for example:
|
||||
|
||||
- ``postgres``
|
||||
- ``rabbitmq``
|
||||
- frontend, like ``node.js``
|
||||
|
||||
To be sure that container is started at the right time,
|
||||
we utilize ``dockerize`` `script <https://github.com/jwilder/dockerize>`_.
|
||||
It is executed inside ``docker/django/entrypoint.sh`` file.
|
||||
|
||||
We start containers with ``tini``.
|
||||
Because this way we have a proper signal handling
|
||||
and eliminate zombie processes.
|
||||
Read the `official docs <https://github.com/krallin/tini>`_ to know more.
|
186
new-github-repos/docs/pages/template/production-checklist.rst
Normal file
186
new-github-repos/docs/pages/template/production-checklist.rst
Normal file
@ -0,0 +1,186 @@
|
||||
.. _`going-to-production`:
|
||||
|
||||
Going to production
|
||||
===================
|
||||
|
||||
This section covers everything you need to know before going to production.
|
||||
|
||||
|
||||
Django
|
||||
------
|
||||
|
||||
Checks
|
||||
~~~~~~
|
||||
|
||||
Before going to production make sure you have checked everything:
|
||||
|
||||
1. Migrations are up-to-date
|
||||
2. Static files are all present
|
||||
3. There are no security or other ``django`` warnings
|
||||
|
||||
Checking migrations, static files,
|
||||
and security is done inside ``ci.sh`` script.
|
||||
|
||||
We check that there are no unapplied migrations:
|
||||
|
||||
.. code :: bash
|
||||
|
||||
python manage.py makemigrations --dry-run --check
|
||||
|
||||
If you have forgotten to create a migration and changed the model,
|
||||
you will see an error on this line.
|
||||
|
||||
We also check that static files can be collected:
|
||||
|
||||
.. code :: bash
|
||||
|
||||
DJANGO_ENV=production python manage.py collectstatic --no-input --dry-run
|
||||
|
||||
However, this check does not cover all the cases.
|
||||
Sometimes ``ManifestStaticFilesStorage`` will fail on real cases,
|
||||
but will pass with ``--dry-run`` option.
|
||||
You can disable ``--dry-run`` option if you know what you are doing.
|
||||
Be careful with this option, when working with auto-uploading
|
||||
your static files to any kind of CDNs.
|
||||
|
||||
That's how we check ``django`` warnings:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
DJANGO_ENV=production python manage.py check --deploy --fail-level WARNING
|
||||
|
||||
These warnings are raised by ``django``
|
||||
when it detects any configuration issues.
|
||||
|
||||
This command should give not warnings or errors.
|
||||
It is bundled into `docker`, so the container will not work with any warnings.
|
||||
|
||||
Static and media files
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We use ``/var/www/django`` folder to store our media
|
||||
and static files in production as ``/var/www/django/static``
|
||||
and ``/var/www/django/media``.
|
||||
Docker uses these two folders as named volumes.
|
||||
And later these volumes are also mounted to ``caddy``
|
||||
with ``ro`` mode so it possible to read their contents.
|
||||
|
||||
To find the exact location of these files on your host
|
||||
you will need to do the following:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker volume ls # to find volumes' names
|
||||
docker volume inspect VOLUME_NAME
|
||||
|
||||
Sometimes storing your media files inside a container is not a good idea.
|
||||
Use ``CDN`` when you have a lot of user content
|
||||
or it is very important not to lose it.
|
||||
There are `helper libraries <http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html>`_
|
||||
to bind ``django`` and these services.
|
||||
|
||||
If you don't need ``media`` files support, just remove the volumes.
|
||||
|
||||
Migrations
|
||||
~~~~~~~~~~
|
||||
|
||||
We do run migration in the ``gunicorn.sh`` by default.
|
||||
Why do we do this? Because that's probably the easiest way to do it.
|
||||
But it clearly has some disadvantages:
|
||||
|
||||
- When scaling your container for multiple nodes you will have multiple
|
||||
threads running the same migrations. And it might be a problem since
|
||||
migrations do not guarantee that it will work this way.
|
||||
- You can perform some operations multiple times
|
||||
- Possible other evil things may happen
|
||||
|
||||
So, what to do in this case?
|
||||
Well, you can do whatever it takes to run migrations in a single thread.
|
||||
For example, you can create a separate container to do just that.
|
||||
Other options are fine as well.
|
||||
|
||||
|
||||
Postgres
|
||||
--------
|
||||
|
||||
Sometimes using ``postgres`` inside a container
|
||||
`is not a good idea <https://myopsblog.wordpress.com/2017/02/06/why-databases-is-not-for-containers/>`_.
|
||||
So, what should be done in this case?
|
||||
|
||||
First of all, move your database ``docker`` service definition
|
||||
inside ``docker-compose.override.yml``.
|
||||
Doing so will not affect development,
|
||||
but will remove database service from production.
|
||||
Next, you will need to specify `extra_hosts <https://docs.docker.com/compose/compose-file/#extra_hosts>`_
|
||||
to contain your ``postgresql`` address.
|
||||
Lastly, you would need to add new hosts to ``pg_hba.conf``.
|
||||
|
||||
`Here <http://winstonkotzan.com/blog/2017/06/01/connecting-to-external-postgres-database-with-docker.html>`_
|
||||
is a nice tutorial about this topic.
|
||||
|
||||
|
||||
Caddy
|
||||
-----
|
||||
|
||||
Let's Encrypt
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
We are using ``Caddy`` and ``Let's Encrypt`` for HTTPS.
|
||||
The Caddy webserver used in the default configuration will get
|
||||
you a valid certificate from ``Let's Encrypt`` and update it automatically.
|
||||
All you need to do to enable this is to make sure
|
||||
that your DNS records are pointing to the server Caddy runs on.
|
||||
|
||||
Read more: `Automatic HTTPS <https://caddyserver.com/docs/automatic-https>`_
|
||||
in Caddy docs.
|
||||
|
||||
Caddyfile validation
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can also run ``-validate`` command to validate ``Caddyfile`` contents.
|
||||
|
||||
Here's it would look like:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker-compose -f docker-compose.yml -f docker/docker-compose.prod.yml
|
||||
run --rm caddy -validate
|
||||
|
||||
This check is not included in the pipeline by default,
|
||||
because it is quite long to start all the machinery for this single check.
|
||||
|
||||
Disabling HTTPS
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
You would need to `disable <https://caddyserver.com/docs/tls>`_
|
||||
``https`` inside ``Caddy`` and in production settings for Django.
|
||||
Because Django itself also redirects to `https`.
|
||||
See `docs <https://docs.djangoproject.com/en/2.2/ref/settings/#secure-ssl-redirect>`_.
|
||||
|
||||
You would also need to disable ``manage.py check``
|
||||
in ``docker/ci.sh``.
|
||||
Otherwise, your application won't start,
|
||||
it would not pass ``django``'s security checks.
|
||||
|
||||
Disabling WWW subdomain
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you for some reason do not require ``www.`` subdomain,
|
||||
then delete ``www.{$DOMAIN_NAME}`` section from ``Caddyfile``.
|
||||
|
||||
Third-Level domains
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You have to disable ``www`` subdomain if
|
||||
your app works on third-level domains like:
|
||||
|
||||
- ``kira.wemake.services``
|
||||
- ``support.myapp.com``
|
||||
|
||||
Otherwise, ``Caddy`` will server redirects to ``www.example.yourdomain.com``.
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
- Django's deployment `checklist <https://docs.djangoproject.com/en/dev/howto/deployment/checklist/#deployment-checklist>`_
|
77
new-github-repos/docs/pages/template/production.rst
Normal file
77
new-github-repos/docs/pages/template/production.rst
Normal file
@ -0,0 +1,77 @@
|
||||
Production
|
||||
==========
|
||||
|
||||
We use different tools and setup for production.
|
||||
We do not fully provide this part with the template. Why?
|
||||
|
||||
1. It requires a lot of server configuration
|
||||
2. It heavily depends on your needs: performance, price, technology, etc
|
||||
3. It is possible to show some vulnerable parts to possible attackers
|
||||
|
||||
So, you will need to deploy your application by yourself.
|
||||
Here, we would like to cover some basic things that are not changed
|
||||
from deployment strategy.
|
||||
|
||||
The easiest deployment strategy for small apps is ``docker-compose`` and
|
||||
``systemd`` inside a host operating system.
|
||||
|
||||
|
||||
Production configuration
|
||||
------------------------
|
||||
|
||||
You will need to specify extra configuration
|
||||
to run ``docker-compose`` in production.
|
||||
Since production build also uses ``caddy``,
|
||||
which is not required into the development build.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker-compose -f docker-compose.yml -f docker/docker-compose.prod.yml up
|
||||
|
||||
|
||||
Pulling pre-built images
|
||||
------------------------
|
||||
|
||||
You will need to pull pre-built images from ``Gitlab`` to run them.
|
||||
How to do that?
|
||||
|
||||
The first step is to create a personal access token for this service.
|
||||
Then, login into your registry with:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker login registry.gitlab.your.domain
|
||||
|
||||
And now you are ready to pull your images:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker pull your-image:latest
|
||||
|
||||
See `official Gitlab docs <https://docs.gitlab.com/ee/user/project/container_registry.html>`_.
|
||||
|
||||
|
||||
Updating already running service
|
||||
--------------------------------
|
||||
|
||||
If you need to update an already running service,
|
||||
them you will have to use ``docker service update``
|
||||
or ``docker stack deploy``.
|
||||
|
||||
Updating existing `service <https://docs.docker.com/engine/reference/commandline/service_update/>`_.
|
||||
Updating existing `stack <https://docs.docker.com/engine/reference/commandline/stack_deploy/>`_.
|
||||
|
||||
Zero-Time Updates
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Zero-Time Updates can be tricky.
|
||||
You need to create containers with the new code, update existing services,
|
||||
wait for the working sessions to be completed, and to shut down old
|
||||
containers.
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
- Production with `docker-compose <https://docs.docker.com/compose/production>`_
|
||||
- `Full tutorial <https://docs.docker.com/get-started>`_
|
114
new-github-repos/docs/pages/template/security.rst
Normal file
114
new-github-repos/docs/pages/template/security.rst
Normal file
@ -0,0 +1,114 @@
|
||||
Security
|
||||
========
|
||||
|
||||
Security is our first priority.
|
||||
We try to make projects as secure as possible.
|
||||
We use a lot of 3rd party tools to achieve that.
|
||||
|
||||
|
||||
Django
|
||||
------
|
||||
|
||||
Django has a lot of `security-specific settings <https://docs.djangoproject.com/en/2.2/topics/security/>`_
|
||||
that are all turned on by default in this template.
|
||||
|
||||
We also :ref:`enforce <going-to-production>` all the best practices
|
||||
by running ``django`` checks inside CI for each commit.
|
||||
|
||||
We also use a set of custom ``django`` apps
|
||||
to enforce even more security rules:
|
||||
|
||||
- `django-axes <https://github.com/jazzband/django-axes>`_ to track and ban repeating access requests
|
||||
- `django-csp <https://github.com/mozilla/django-csp>`_ to enforce `Content-Security Policy <https://www.w3.org/TR/CSP/>`_ for our webpages
|
||||
- `django-http-referrer-policy <https://django-referrer-policy.readthedocs.io>`_ to enforce `Referrer Policy <https://www.w3.org/TR/referrer-policy/>`_ for our webpages
|
||||
|
||||
And there are also some awesome extensions that are not included:
|
||||
|
||||
- `django-honeypot <https://github.com/jamesturk/django-honeypot>`_ - django application that provides utilities for preventing automated form spam
|
||||
|
||||
Passwords
|
||||
~~~~~~~~~
|
||||
|
||||
We use strong algorithms for password hashing:
|
||||
``bcrypt``, ``PBKDF2`` and ``Argon2`` which are known to be secure enough.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
We use `poetry <https://poetry.eustace.io/>`_ which ensures
|
||||
that all the dependencies hashes match during the installation process.
|
||||
Otherwise, the build will fail.
|
||||
So, it is almost impossible to replace an already existing package
|
||||
with a malicious one.
|
||||
|
||||
We also use `safety <https://github.com/pyupio/safety>`_
|
||||
to analyze vulnerable dependencies to prevent the build
|
||||
to go to the production with known unsafe dependencies.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
safety check
|
||||
|
||||
We also use `Github security alerts <https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/>`_
|
||||
for our main template repository.
|
||||
|
||||
|
||||
Static analysis
|
||||
---------------
|
||||
|
||||
We use ``wemake-python-styleguide`` which
|
||||
includes `bandit <https://pypi.org/project/bandit/>`_ security checks inside.
|
||||
|
||||
You can also install `pyt <https://pyt.readthedocs.io>`_
|
||||
which is not included by default.
|
||||
It will include even more static checks for
|
||||
``sql`` injections, ``xss`` and others.
|
||||
|
||||
|
||||
Dynamic analysis
|
||||
----------------
|
||||
|
||||
You can monitor your running application to detect anomalous activities.
|
||||
Tools to consider:
|
||||
|
||||
- `dagda <https://github.com/eliasgranderubio/dagda>`_ - a tool to perform static analysis of known vulnerabilities, trojans, viruses, malware & other malicious threats in docker images/containers and to monitor the docker daemon and running docker containers for detecting anomalous activities
|
||||
|
||||
All the tools above are not included into this template.
|
||||
You have to install them by yourself.
|
||||
|
||||
|
||||
Secrets
|
||||
-------
|
||||
|
||||
We store secrets separately from code. So, it is harder for them to leak.
|
||||
However, we encourage to use tools like
|
||||
`truffleHog <https://github.com/dxa4481/truffleHog>`_ or `detect-secrets <https://github.com/Yelp/detect-secrets>`_ inside your workflow.
|
||||
|
||||
You can also turn on `Gitlab secrets checker <https://docs.gitlab.com/ee/push_rules/push_rules.html#prevent-pushing-secrets-to-the-repository>`_ which we highly recommend.
|
||||
|
||||
|
||||
Audits
|
||||
------
|
||||
|
||||
The only way to be sure that your app is secure
|
||||
is to constantly audit it in production.
|
||||
|
||||
There are different tools to help you:
|
||||
|
||||
- `twa <https://github.com/trailofbits/twa>`_ - tiny web auditor that has a lot of security checks for the webpages
|
||||
- `XSStrike <https://github.com/s0md3v/XSStrike>`_ - automated tool to check that your application is not vulnerable to ``xss`` errors
|
||||
- `docker-bench <https://github.com/docker/docker-bench-security>`_ - a script that checks for dozens of common best-practices around deploying Docker containers in production
|
||||
- `lynis <https://cisofy.com/lynis/>`_ - a battle-tested security tool for systems running Linux, macOS, or Unix-based operating system
|
||||
- `trivy <https://github.com/knqyf263/trivy>`_ - a simple and comprehensive vulnerability scanner for containers
|
||||
|
||||
But, even after all you attempts to secure your application,
|
||||
it **won't be 100% safe**. Do not fall into this false feeling of security.
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
- `Open Web Application Security Project <https://www.owasp.org/images/3/33/OWASP_Application_Security_Verification_Standard_3.0.1.pdf>`_
|
||||
- `Docker security <https://docs.docker.com/engine/security/security/>`_
|
||||
- `AppArmor <https://docs.docker.com/engine/security/apparmor/>`_ and `bane <https://github.com/genuinetools/bane>`_
|
111
new-github-repos/docs/pages/template/testing.rst
Normal file
111
new-github-repos/docs/pages/template/testing.rst
Normal file
@ -0,0 +1,111 @@
|
||||
Testing
|
||||
=======
|
||||
|
||||
We try to keep our quality standards high.
|
||||
So, we use different tools to make this possible.
|
||||
|
||||
We use `mypy <http://mypy-lang.org/>`_ for optional
|
||||
static typing.
|
||||
We run tests with `pytest <https://pytest.org/>`_ framework.
|
||||
|
||||
|
||||
pytest
|
||||
------
|
||||
|
||||
``pytest`` is the main tool for test discovery, collection, and execution.
|
||||
It is configured inside ``setup.cfg`` file.
|
||||
|
||||
We use a lot of ``pytest`` plugins that enhance our development experience.
|
||||
List of these plugins is available inside ``pyproject.toml`` file.
|
||||
|
||||
Running:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pytest
|
||||
|
||||
We also have some options that are set on each run via ``--addopts``
|
||||
inside the ``setup.cfg`` file.
|
||||
|
||||
Plugins
|
||||
~~~~~~~
|
||||
|
||||
We use different ``pytest`` plugins to make our testing process better.
|
||||
Here's the full list of things we use:
|
||||
|
||||
- `pytest-django`_ - plugin that introduce a lot of ``django`` specific
|
||||
helpers, fixtures, and configuration
|
||||
- `django-test-migrations`_ - plugin to test Django migrations and their order
|
||||
- `pytest-cov`_ - plugin to measure test coverage
|
||||
- `pytest-randomly`_ - plugin to execute tests in random order and
|
||||
also set predictable random seed, so you can easily debug
|
||||
what went wrong for tests that rely on random behavior
|
||||
- `pytest-deadfixtures`_ - plugin to find unused or duplicate fixtures
|
||||
- `pytest-timeout`_ - plugin to raise errors for tests
|
||||
that take too long to finish, this way you can control test execution speed
|
||||
- `pytest-testmon`_ - plugin for `Test Driven Development`_ which executes
|
||||
tests that are affected by your code changes
|
||||
|
||||
.. _pytest-django: https://github.com/pytest-dev/pytest-django
|
||||
.. _django-test-migrations: https://github.com/wemake-services/django-test-migrations
|
||||
.. _pytest-cov: https://github.com/pytest-dev/pytest-cov
|
||||
.. _pytest-randomly: https://github.com/pytest-dev/pytest-randomly
|
||||
.. _pytest-deadfixtures: https://github.com/jllorencetti/pytest-deadfixtures
|
||||
.. _pytest-timeout: https://pypi.org/project/pytest-timeout
|
||||
.. _pytest-testmon: https://github.com/tarpas/pytest-testmon
|
||||
.. _`Test Driven Development`: https://en.wikipedia.org/wiki/Test-driven_development
|
||||
|
||||
Tweaking tests performance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are several options you can provide or remove to make your tests faster:
|
||||
|
||||
- You can use ``pytest-xdist`` together with
|
||||
``-n auto`` to schedule several numbers of workers,
|
||||
sometimes when there are a lot of tests it may increase the testing speed.
|
||||
But on a small project with a small amount of test it just
|
||||
gives you an overhead, so removing it (together with `--boxed`)
|
||||
will boost your testing performance
|
||||
- If there are a lot of tests with database access
|
||||
it may be wise to add
|
||||
`--reuse-db option <https://pytest-django.readthedocs.io/en/latest/database.html#example-work-flow-with-reuse-db-and-create-db>`_,
|
||||
so ``django`` won't recreate database on each test
|
||||
- If there are a lot of migrations to perform you may also add
|
||||
`--nomigrations option <https://pytest-django.readthedocs.io/en/latest/database.html#nomigrations-disable-django-1-7-migrations>`_,
|
||||
so ``django`` won't run all the migrations
|
||||
and instead will inspect and create models directly
|
||||
- Removing ``coverage``. Sometimes that an option.
|
||||
When running tests in TDD style why would you need such a feature?
|
||||
So, coverage will be calculated when you will ask for it.
|
||||
That's a huge speed up
|
||||
- Removing linters. Sometimes you may want to split linting and testing phases.
|
||||
This might be useful when you have a lot of tests, and you want to run
|
||||
linters before, so it won't fail your complex testing pyramid with a simple
|
||||
whitespace violation
|
||||
|
||||
|
||||
mypy
|
||||
----
|
||||
|
||||
Running ``mypy`` is required before any commit:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
mypy server tests/**/*.py
|
||||
|
||||
This will eliminate a lot of possible ``TypeError`` and other issues
|
||||
in both `server/` and `tests/` directories.
|
||||
We use `tests/**/*.py` because `tests/` is not a python package,
|
||||
so it is not importable.
|
||||
|
||||
However, this will not make code 100% safe from errors.
|
||||
So, both the testing and review process are still required.
|
||||
|
||||
``mypy`` is configured via ``setup.cfg``.
|
||||
Read the `docs <https://mypy.readthedocs.io/en/latest/>`_
|
||||
for more information.
|
||||
|
||||
We also use `django-stubs <https://github.com/typeddjango/django-stubs>`_
|
||||
to type ``django`` internals.
|
||||
This package is optional and can be removed,
|
||||
if you don't want to type your ``django`` for some reason.
|
47
new-github-repos/docs/pages/template/troubleshooting.rst
Normal file
47
new-github-repos/docs/pages/template/troubleshooting.rst
Normal file
@ -0,0 +1,47 @@
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
This section is about some of the problems you may encounter and
|
||||
how to solve these problems.
|
||||
|
||||
|
||||
Docker
|
||||
------
|
||||
|
||||
Pillow
|
||||
~~~~~~
|
||||
|
||||
If you want to install ``Pillow`` that you should
|
||||
add this to dockerfile and rebuild image:
|
||||
|
||||
- ``RUN apk add jpeg-dev zlib-dev``
|
||||
- ``LIBRARY_PATH=/lib:/usr/lib /bin/sh -c "poetry install ..."``
|
||||
|
||||
See `<https://github.com/python-pillow/Pillow/issues/1763>`_
|
||||
|
||||
Root owns build artifacts
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This happens on some systems.
|
||||
It happens because build happens in ``docker`` as the ``root`` user.
|
||||
The fix is to pass current ``UID`` to ``docker``.
|
||||
See `<https://github.com/wemake-services/wemake-django-template/issues/345>`_.
|
||||
|
||||
MacOS performance
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you use the MacOS you
|
||||
know that you have problems with disk performance.
|
||||
Starting and restarting an application is slower than with Linux
|
||||
(it's very noticeable for project with large codebase).
|
||||
For particular solve this problem add ``:delegated`` to each
|
||||
your volumes in ``docker-compose.yml`` file.
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data:delegated
|
||||
|
||||
For more information, you can look at the
|
||||
`docker documents <https://docs.docker.com/docker-for-mac/osxfs-caching/>`_
|
||||
and a good `article <https://medium.com/@TomKeur/how-get-better-disk-performance-in-docker-for-mac-2ba1244b5b70>`_.
|
51
new-github-repos/docs/pages/template/upgrading-template.rst
Normal file
51
new-github-repos/docs/pages/template/upgrading-template.rst
Normal file
@ -0,0 +1,51 @@
|
||||
Upgrading template
|
||||
==================
|
||||
|
||||
Upgrading your project to be up-to-date with this template is a primary goal.
|
||||
This is achieved by manually applying ``diff`` to your existing code.
|
||||
|
||||
``diff`` can be viewed from the project's ``README.md``.
|
||||
See `an example <https://github.com/wemake-services/wemake-django-template/compare/91188fc4b89bd4989a0ead3d156a4619644965b0...master>`_.
|
||||
|
||||
When the upgrade is applied just change the commit hash in your template
|
||||
to the most recent one.
|
||||
|
||||
|
||||
Versions
|
||||
--------
|
||||
|
||||
Sometimes, when we break something heavily, we create a version.
|
||||
That's is required for our users, so they can use old releases to create
|
||||
projects as they used to be a long time ago.
|
||||
|
||||
However, we do not officially support older versions.
|
||||
And we do not recommend to use them.
|
||||
|
||||
A full list of versions can be `found here <https://github.com/wemake-services/wemake-django-template/releases>`_.
|
||||
|
||||
|
||||
Migration guides
|
||||
----------------
|
||||
|
||||
Each time we create a new version, we also provide a migration guide.
|
||||
What is a migration guide?
|
||||
It is something you have to do to your project
|
||||
other than just copy-pasting diffs from new versions.
|
||||
|
||||
Goodbye, pipenv!
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
This version requires a manual migration step.
|
||||
|
||||
1. You need to install ``poetry``
|
||||
2. You need to create a new ``pyproject.toml`` file with ``poetry init``
|
||||
3. You need to adjust name, version, description, and authors meta fields
|
||||
4. You need to copy-paste dependencies from ``Pipfile`` to ``pyproject.toml``
|
||||
5. You need to set correct version for each dependency in the list,
|
||||
use ``"^x.y"`` `notation <https://python-poetry.org/docs/dependency-specification/#caret-requirements>`_
|
||||
6. You need to adjust ``[build-system]`` tag and ``POETRY_VERSION`` variable
|
||||
to fit your ``poetry`` version
|
||||
7. Create ``poetry.lock`` file with ``poetry lock``
|
||||
|
||||
It should be fine! You may, however, experience some bugs related to different
|
||||
dependency version resolution mechanisms. But, ``poetry`` does it better.
|
6
new-github-repos/docs/requirements.txt
Normal file
6
new-github-repos/docs/requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# This file is required since we use ReadTheDocs services.
|
||||
# If it is not used, feel free to delete it.
|
||||
|
||||
sphinx==4.0.3
|
||||
sphinx_autodoc_typehints==1.12.0
|
||||
tomlkit==0.7.2
|
0
new-github-repos/locale/.gitkeep
Normal file
0
new-github-repos/locale/.gitkeep
Normal file
31
new-github-repos/manage.py
Executable file
31
new-github-repos/manage.py
Executable file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Main function.
|
||||
|
||||
It does several things:
|
||||
1. Sets default settings module, if it is not set
|
||||
2. Warns if Django is not installed
|
||||
3. Executes any given command
|
||||
"""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
|
||||
|
||||
try:
|
||||
from django.core import management # noqa: WPS433
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and " +
|
||||
'available on your PYTHONPATH environment variable? Did you ' +
|
||||
'forget to activate a virtual environment?',
|
||||
)
|
||||
|
||||
management.execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
2719
new-github-repos/poetry.lock
generated
Normal file
2719
new-github-repos/poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
73
new-github-repos/pyproject.toml
Normal file
73
new-github-repos/pyproject.toml
Normal file
@ -0,0 +1,73 @@
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
||||
[tool.nitpick]
|
||||
|
||||
|
||||
[tool.poetry]
|
||||
name = "github-repos"
|
||||
description = "github repos"
|
||||
version = "0.1.0"
|
||||
readme = "README.md"
|
||||
authors = ["balsh"]
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "3.8.9"
|
||||
django = "^3"
|
||||
django-split-settings = "^1.0"
|
||||
django-axes = "^5.20"
|
||||
django-csp = "^3.7"
|
||||
django-health-check = "^3.16"
|
||||
django-http-referrer-policy = "^1.1"
|
||||
django-permissions-policy = "^4.1"
|
||||
django-stubs-ext = "^0.2"
|
||||
|
||||
psycopg2-binary = "<2.9"
|
||||
gunicorn = "^20.0"
|
||||
python-decouple = "^3.4"
|
||||
bcrypt = "^3.2"
|
||||
structlog = "^21.1"
|
||||
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
django-debug-toolbar = "^3.2"
|
||||
django-querycount = "^0.7"
|
||||
django-migration-linter = "^3.0"
|
||||
django-extra-checks = "^0.11"
|
||||
django-coverage-plugin = "^2.0"
|
||||
nplusone = "^1.0"
|
||||
|
||||
wemake-python-styleguide = "^0.15"
|
||||
flake8-pytest-style = "^1.5"
|
||||
flake8-django = "^1.1"
|
||||
flake8-logging-format = "^0.6"
|
||||
nitpick = "^0.26"
|
||||
|
||||
pytest = "^6.2"
|
||||
pytest-django = "^4.4"
|
||||
pytest-cov = "^2.12"
|
||||
pytest-randomly = "^3.8"
|
||||
pytest-deadfixtures = "^2.2"
|
||||
pytest-testmon = "^1.1"
|
||||
pytest-timeout = "^1.4"
|
||||
django-test-migrations = "^1.1"
|
||||
hypothesis = "^6.14"
|
||||
|
||||
mypy = "^0.910"
|
||||
django-stubs = "^1.8"
|
||||
|
||||
sphinx = "^4.0"
|
||||
sphinx-autodoc-typehints = "^1.12"
|
||||
tomlkit = "^0.7"
|
||||
doc8 = "^0.8"
|
||||
|
||||
yamllint = "^1.26"
|
||||
safety = "^1.10"
|
||||
dotenv-linter = "^0.2"
|
||||
polint = "^0.4"
|
||||
dennis = "^0.9"
|
||||
dump-env = "^1.3"
|
||||
ipython = "^7.25"
|
0
new-github-repos/server/__init__.py
Normal file
0
new-github-repos/server/__init__.py
Normal file
0
new-github-repos/server/apps/__init__.py
Normal file
0
new-github-repos/server/apps/__init__.py
Normal file
0
new-github-repos/server/apps/main/__init__.py
Normal file
0
new-github-repos/server/apps/main/__init__.py
Normal file
8
new-github-repos/server/apps/main/admin.py
Normal file
8
new-github-repos/server/apps/main/admin.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from server.apps.main.models import BlogPost
|
||||
|
||||
|
||||
@admin.register(BlogPost)
|
||||
class BlogPostAdmin(admin.ModelAdmin[BlogPost]):
|
||||
"""Admin panel example for ``BlogPost`` model."""
|
11
new-github-repos/server/apps/main/logic/__init__.py
Normal file
11
new-github-repos/server/apps/main/logic/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
"""
|
||||
This package is a place for your business logic.
|
||||
|
||||
Please, do not create any other files inside your app package.
|
||||
Place all files here, including: logic, forms, serializers.
|
||||
|
||||
Decoupling is a good thing. We need more of that.
|
||||
|
||||
Try using https://github.com/dry-python/
|
||||
It is awesome for writing business logic!
|
||||
"""
|
35
new-github-repos/server/apps/main/migrations/0001_initial.py
Normal file
35
new-github-repos/server/apps/main/migrations/0001_initial.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Generated by Django 2.2.7 on 2019-11-24 11:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"""Initial migration that creates the example BlogPost model."""
|
||||
|
||||
initial = True
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlogPost',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name='ID',
|
||||
),
|
||||
),
|
||||
('title', models.CharField(max_length=80)),
|
||||
('body', models.TextField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'BlogPost',
|
||||
'verbose_name_plural': 'BlogPosts',
|
||||
},
|
||||
),
|
||||
]
|
33
new-github-repos/server/apps/main/models.py
Normal file
33
new-github-repos/server/apps/main/models.py
Normal file
@ -0,0 +1,33 @@
|
||||
import textwrap
|
||||
from typing import Final, final
|
||||
|
||||
from django.db import models
|
||||
|
||||
#: That's how constants should be defined.
|
||||
_POST_TITLE_MAX_LENGTH: Final = 80
|
||||
|
||||
|
||||
@final
|
||||
class BlogPost(models.Model):
|
||||
"""
|
||||
This model is used just as an example.
|
||||
|
||||
With it we show how one can:
|
||||
- Use fixtures and factories
|
||||
- Use migrations testing
|
||||
|
||||
"""
|
||||
|
||||
title = models.CharField(max_length=_POST_TITLE_MAX_LENGTH)
|
||||
body = models.TextField()
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta(object):
|
||||
verbose_name = 'BlogPost' # You can probably use `gettext` for this
|
||||
verbose_name_plural = 'BlogPosts'
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""All django models should have this method."""
|
||||
return textwrap.wrap(self.title, _POST_TITLE_MAX_LENGTH // 4)[0]
|
29
new-github-repos/server/apps/main/static/main/css/index.css
Normal file
29
new-github-repos/server/apps/main/static/main/css/index.css
Normal file
@ -0,0 +1,29 @@
|
||||
body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.wemake-services-body {
|
||||
height: 95vh;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wemake-services-logo {
|
||||
max-width: 250px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.github-corner img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: 0;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
46
new-github-repos/server/apps/main/templates/main/index.html
Normal file
46
new-github-repos/server/apps/main/templates/main/index.html
Normal file
File diff suppressed because one or more lines are too long
9
new-github-repos/server/apps/main/urls.py
Normal file
9
new-github-repos/server/apps/main/urls.py
Normal file
@ -0,0 +1,9 @@
|
||||
from django.urls import path
|
||||
|
||||
from server.apps.main.views import index
|
||||
|
||||
app_name = 'main'
|
||||
|
||||
urlpatterns = [
|
||||
path('hello/', index, name='hello'),
|
||||
]
|
12
new-github-repos/server/apps/main/views.py
Normal file
12
new-github-repos/server/apps/main/views.py
Normal file
@ -0,0 +1,12 @@
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
def index(request: HttpRequest) -> HttpResponse:
|
||||
"""
|
||||
Main (or index) view.
|
||||
|
||||
Returns rendered default page to the user.
|
||||
Typed with the help of ``django-stubs`` project.
|
||||
"""
|
||||
return render(request, 'main/index.html')
|
39
new-github-repos/server/settings/__init__.py
Normal file
39
new-github-repos/server/settings/__init__.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""
|
||||
This is a django-split-settings main file.
|
||||
|
||||
For more information read this:
|
||||
https://github.com/sobolevn/django-split-settings
|
||||
https://sobolevn.me/2017/04/managing-djangos-settings
|
||||
|
||||
To change settings file:
|
||||
`DJANGO_ENV=production python manage.py runserver`
|
||||
"""
|
||||
|
||||
from os import environ
|
||||
|
||||
import django_stubs_ext
|
||||
from split_settings.tools import include, optional
|
||||
|
||||
# Monkeypatching Django, so stubs will work for all generics,
|
||||
# see: https://github.com/typeddjango/django-stubs
|
||||
django_stubs_ext.monkeypatch()
|
||||
|
||||
# Managing environment via `DJANGO_ENV` variable:
|
||||
environ.setdefault('DJANGO_ENV', 'development')
|
||||
_ENV = environ['DJANGO_ENV']
|
||||
|
||||
_base_settings = (
|
||||
'components/common.py',
|
||||
'components/logging.py',
|
||||
'components/csp.py',
|
||||
'components/caches.py',
|
||||
|
||||
# Select the right env:
|
||||
'environments/{0}.py'.format(_ENV),
|
||||
|
||||
# Optionally override some settings:
|
||||
optional('environments/local.py'),
|
||||
)
|
||||
|
||||
# Include settings:
|
||||
include(*_base_settings)
|
11
new-github-repos/server/settings/components/__init__.py
Normal file
11
new-github-repos/server/settings/components/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
from pathlib import Path
|
||||
|
||||
from decouple import AutoConfig
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR.joinpath('some')
|
||||
# `pathlib` is better than writing: dirname(dirname(dirname(__file__)))
|
||||
BASE_DIR = Path.cwd().parent.parent.parent.parent
|
||||
|
||||
# Loading `.env` files
|
||||
# See docs: https://gitlab.com/mkleehammer/autoconfig
|
||||
config = AutoConfig(search_path=BASE_DIR.joinpath('config'))
|
16
new-github-repos/server/settings/components/caches.py
Normal file
16
new-github-repos/server/settings/components/caches.py
Normal file
@ -0,0 +1,16 @@
|
||||
# Caching
|
||||
# https://docs.djangoproject.com/en/2.2/topics/cache/
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
# TODO: use some other cache in production,
|
||||
# like https://github.com/jazzband/django-redis
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# django-axes
|
||||
# https://django-axes.readthedocs.io/en/latest/4_configuration.html#configuring-caches
|
||||
|
||||
AXES_CACHE = 'default'
|
201
new-github-repos/server/settings/components/common.py
Normal file
201
new-github-repos/server/settings/components/common.py
Normal file
@ -0,0 +1,201 @@
|
||||
"""
|
||||
Django settings for server project.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.2/topics/settings/
|
||||
|
||||
For the full list of settings and their config, see
|
||||
https://docs.djangoproject.com/en/2.2/ref/settings/
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Tuple, Union
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from server.settings.components import BASE_DIR, config
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
|
||||
|
||||
SECRET_KEY = config('DJANGO_SECRET_KEY')
|
||||
|
||||
# Application definition:
|
||||
|
||||
INSTALLED_APPS: Tuple[str, ...] = (
|
||||
# Your apps go here:
|
||||
'server.apps.main',
|
||||
|
||||
# Default django apps:
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
# django-admin:
|
||||
'django.contrib.admin',
|
||||
'django.contrib.admindocs',
|
||||
|
||||
# Security:
|
||||
'axes',
|
||||
|
||||
# Health checks:
|
||||
# You may want to enable other checks as well,
|
||||
# see: https://github.com/KristianOellegaard/django-health-check
|
||||
'health_check',
|
||||
'health_check.db',
|
||||
'health_check.cache',
|
||||
'health_check.storage',
|
||||
)
|
||||
|
||||
MIDDLEWARE: Tuple[str, ...] = (
|
||||
# Content Security Policy:
|
||||
'csp.middleware.CSPMiddleware',
|
||||
|
||||
# Django:
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
# django-permissions-policy
|
||||
'django_permissions_policy.PermissionsPolicyMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
|
||||
# Axes:
|
||||
'axes.middleware.AxesMiddleware',
|
||||
|
||||
# Django HTTP Referrer Policy:
|
||||
'django_http_referrer_policy.middleware.ReferrerPolicyMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'server.urls'
|
||||
|
||||
WSGI_APPLICATION = 'server.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': config('POSTGRES_DB'),
|
||||
'USER': config('POSTGRES_USER'),
|
||||
'PASSWORD': config('POSTGRES_PASSWORD'),
|
||||
'HOST': config('DJANGO_DATABASE_HOST'),
|
||||
'PORT': config('DJANGO_DATABASE_PORT', cast=int),
|
||||
'CONN_MAX_AGE': config('CONN_MAX_AGE', cast=int, default=60),
|
||||
'OPTIONS': {
|
||||
'connect_timeout': 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/2.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
|
||||
LANGUAGES = (
|
||||
('en', _('English')),
|
||||
('ru', _('Russian')),
|
||||
)
|
||||
|
||||
LOCALE_PATHS = (
|
||||
'locale/',
|
||||
)
|
||||
|
||||
USE_TZ = True
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
)
|
||||
|
||||
|
||||
# Templates
|
||||
# https://docs.djangoproject.com/en/2.2/ref/templates/api
|
||||
|
||||
TEMPLATES = [{
|
||||
'APP_DIRS': True,
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [
|
||||
# Contains plain text templates, like `robots.txt`:
|
||||
BASE_DIR.joinpath('server', 'templates'),
|
||||
],
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
# Default template context processors:
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.i18n',
|
||||
'django.template.context_processors.media',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django.template.context_processors.request',
|
||||
],
|
||||
},
|
||||
}]
|
||||
|
||||
|
||||
# Media files
|
||||
# Media root dir is commonly changed in production
|
||||
# (see development.py and production.py).
|
||||
# https://docs.djangoproject.com/en/2.2/topics/files/
|
||||
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = BASE_DIR.joinpath('media')
|
||||
|
||||
|
||||
# Django authentication system
|
||||
# https://docs.djangoproject.com/en/2.2/topics/auth/
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'axes.backends.AxesBackend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
||||
'django.contrib.auth.hashers.Argon2PasswordHasher',
|
||||
]
|
||||
|
||||
|
||||
# Security
|
||||
# https://docs.djangoproject.com/en/2.2/topics/security/
|
||||
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
|
||||
# https://github.com/DmytroLitvinov/django-http-referrer-policy
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
|
||||
REFERRER_POLICY = 'same-origin'
|
||||
|
||||
# https://github.com/adamchainz/django-permissions-policy#setting
|
||||
PERMISSIONS_POLICY: Dict[str, Union[str, List[str]]] = {} # noqa: WPS234
|
||||
|
||||
|
||||
# Timeouts
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-EMAIL_TIMEOUT
|
||||
|
||||
EMAIL_TIMEOUT = 5
|
15
new-github-repos/server/settings/components/csp.py
Normal file
15
new-github-repos/server/settings/components/csp.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""
|
||||
This file contains a definition for Content-Security-Policy headers.
|
||||
|
||||
Read more about it:
|
||||
https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||
|
||||
We are using `django-csp` to provide these headers.
|
||||
Docs: https://github.com/mozilla/django-csp
|
||||
"""
|
||||
|
||||
CSP_SCRIPT_SRC = ("'self'",)
|
||||
CSP_IMG_SRC = ("'self'",)
|
||||
CSP_FONT_SRC = ("'self'",)
|
||||
CSP_STYLE_SRC = ("'self'",)
|
||||
CSP_DEFAULT_SRC = ("'none'",)
|
77
new-github-repos/server/settings/components/logging.py
Normal file
77
new-github-repos/server/settings/components/logging.py
Normal file
@ -0,0 +1,77 @@
|
||||
# Logging
|
||||
# https://docs.djangoproject.com/en/2.2/topics/logging/
|
||||
|
||||
# See also:
|
||||
# 'Do not log' by Nikita Sobolev (@sobolevn)
|
||||
# https://sobolevn.me/2020/03/do-not-log
|
||||
|
||||
import structlog
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
||||
# We use these formatters in our `'handlers'` configuration.
|
||||
# Probably, you won't need to modify these lines.
|
||||
# Unless, you know what you are doing.
|
||||
'formatters': {
|
||||
'json_formatter': {
|
||||
'()': structlog.stdlib.ProcessorFormatter,
|
||||
'processor': structlog.processors.JSONRenderer(),
|
||||
},
|
||||
'console': {
|
||||
'()': structlog.stdlib.ProcessorFormatter,
|
||||
'processor': structlog.processors.KeyValueRenderer(
|
||||
key_order=['timestamp', 'level', 'event', 'logger'],
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
# You can easily swap `key/value` (default) output and `json` ones.
|
||||
# Use `'json_console'` if you need `json` logs.
|
||||
'handlers': {
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'console',
|
||||
},
|
||||
'json_console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'json_formatter',
|
||||
},
|
||||
},
|
||||
|
||||
# These loggers are required by our app:
|
||||
# - django is required when using `logger.getLogger('django')`
|
||||
# - security is required by `axes`
|
||||
'loggers': {
|
||||
'django': {
|
||||
'handlers': ['console'],
|
||||
'propagate': True,
|
||||
'level': 'INFO',
|
||||
},
|
||||
'security': {
|
||||
'handlers': ['console'],
|
||||
'level': 'ERROR',
|
||||
'propagate': False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
structlog.configure(
|
||||
processors=[
|
||||
structlog.stdlib.filter_by_level,
|
||||
structlog.processors.TimeStamper(fmt='iso'),
|
||||
structlog.stdlib.add_logger_name,
|
||||
structlog.stdlib.add_log_level,
|
||||
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||
structlog.processors.StackInfoRenderer(),
|
||||
structlog.processors.format_exc_info,
|
||||
structlog.processors.UnicodeDecoder(),
|
||||
structlog.processors.ExceptionPrettyPrinter(),
|
||||
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
||||
],
|
||||
context_class=structlog.threadlocal.wrap_dict(dict),
|
||||
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||
wrapper_class=structlog.stdlib.BoundLogger,
|
||||
cache_logger_on_first_use=True,
|
||||
)
|
@ -0,0 +1,2 @@
|
||||
|
||||
"""Overriding settings based on the environment."""
|
150
new-github-repos/server/settings/environments/development.py
Normal file
150
new-github-repos/server/settings/environments/development.py
Normal file
@ -0,0 +1,150 @@
|
||||
"""
|
||||
This file contains all the settings that defines the development server.
|
||||
|
||||
SECURITY WARNING: don't run with debug turned on in production!
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from server.settings.components import config
|
||||
from server.settings.components.common import (
|
||||
DATABASES,
|
||||
INSTALLED_APPS,
|
||||
MIDDLEWARE,
|
||||
)
|
||||
|
||||
# Setting the development status:
|
||||
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
config('DOMAIN_NAME'),
|
||||
'localhost',
|
||||
'0.0.0.0', # noqa: S104
|
||||
'127.0.0.1',
|
||||
'[::1]',
|
||||
]
|
||||
|
||||
|
||||
# Installed apps for development only:
|
||||
|
||||
INSTALLED_APPS += (
|
||||
# Better debug:
|
||||
'debug_toolbar',
|
||||
'nplusone.ext.django',
|
||||
|
||||
# Linting migrations:
|
||||
'django_migration_linter',
|
||||
|
||||
# django-test-migrations:
|
||||
'django_test_migrations.contrib.django_checks.AutoNames',
|
||||
# This check might be useful in production as well,
|
||||
# so it might be a good idea to move `django-test-migrations`
|
||||
# to prod dependencies and use this check in the main `settings.py`.
|
||||
# This will check that your database is configured properly,
|
||||
# when you run `python manage.py check` before deploy.
|
||||
'django_test_migrations.contrib.django_checks.DatabaseConfiguration',
|
||||
|
||||
# django-extra-checks:
|
||||
'extra_checks',
|
||||
)
|
||||
|
||||
|
||||
# Static files:
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-STATICFILES_DIRS
|
||||
|
||||
STATICFILES_DIRS: List[str] = []
|
||||
|
||||
|
||||
# Django debug toolbar:
|
||||
# https://django-debug-toolbar.readthedocs.io
|
||||
|
||||
MIDDLEWARE += (
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
|
||||
# https://github.com/bradmontgomery/django-querycount
|
||||
# Prints how many queries were executed, useful for the APIs.
|
||||
'querycount.middleware.QueryCountMiddleware',
|
||||
)
|
||||
|
||||
|
||||
def _custom_show_toolbar(request):
|
||||
"""Only show the debug toolbar to users with the superuser flag."""
|
||||
return DEBUG and request.user.is_superuser
|
||||
|
||||
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
'SHOW_TOOLBAR_CALLBACK':
|
||||
'server.settings.environments.development._custom_show_toolbar',
|
||||
}
|
||||
|
||||
# This will make debug toolbar to work with django-csp,
|
||||
# since `ddt` loads some scripts from `ajax.googleapis.com`:
|
||||
CSP_SCRIPT_SRC = ("'self'", 'ajax.googleapis.com')
|
||||
CSP_IMG_SRC = ("'self'", 'data:')
|
||||
CSP_CONNECT_SRC = ("'self'",)
|
||||
|
||||
|
||||
# nplusone
|
||||
# https://github.com/jmcarp/nplusone
|
||||
|
||||
# Should be the first in line:
|
||||
MIDDLEWARE = ( # noqa: WPS440
|
||||
'nplusone.ext.django.NPlusOneMiddleware',
|
||||
) + MIDDLEWARE
|
||||
|
||||
# Logging N+1 requests:
|
||||
NPLUSONE_RAISE = True # comment out if you want to allow N+1 requests
|
||||
NPLUSONE_LOGGER = logging.getLogger('django')
|
||||
NPLUSONE_LOG_LEVEL = logging.WARN
|
||||
NPLUSONE_WHITELIST = [
|
||||
{'model': 'admin.*'},
|
||||
]
|
||||
|
||||
|
||||
# django-test-migrations
|
||||
# https://github.com/wemake-services/django-test-migrations
|
||||
|
||||
# Set of badly named migrations to ignore:
|
||||
DTM_IGNORED_MIGRATIONS = frozenset((
|
||||
('axes', '*'),
|
||||
))
|
||||
|
||||
|
||||
# django-extra-checks
|
||||
# https://github.com/kalekseev/django-extra-checks
|
||||
|
||||
EXTRA_CHECKS = {
|
||||
'checks': [
|
||||
# Forbid `unique_together`:
|
||||
'no-unique-together',
|
||||
# Require non empty `upload_to` argument:
|
||||
'field-file-upload-to',
|
||||
# Use the indexes option instead:
|
||||
'no-index-together',
|
||||
# Each model must be registered in admin:
|
||||
'model-admin',
|
||||
# FileField/ImageField must have non empty `upload_to` argument:
|
||||
'field-file-upload-to',
|
||||
# Text fields shouldn't use `null=True`:
|
||||
'field-text-null',
|
||||
# Prefer using BooleanField(null=True) instead of NullBooleanField:
|
||||
'field-boolean-null',
|
||||
# Don't pass `null=False` to model fields (this is django default)
|
||||
'field-null',
|
||||
# ForeignKey fields must specify db_index explicitly if used in
|
||||
# other indexes:
|
||||
{'id': 'field-foreign-key-db-index', 'when': 'indexes'},
|
||||
# If field nullable `(null=True)`,
|
||||
# then default=None argument is redundant and should be removed:
|
||||
'field-default-null',
|
||||
# Fields with choices must have companion CheckConstraint
|
||||
# to enforce choices on database level
|
||||
'field-choices-constraint',
|
||||
],
|
||||
}
|
||||
|
||||
# Disable persistent DB connections
|
||||
# https://docs.djangoproject.com/en/2.2/ref/databases/#caveats
|
||||
DATABASES['default']['CONN_MAX_AGE'] = 0
|
@ -0,0 +1 @@
|
||||
"""Override any custom settings here."""
|
75
new-github-repos/server/settings/environments/production.py
Normal file
75
new-github-repos/server/settings/environments/production.py
Normal file
@ -0,0 +1,75 @@
|
||||
"""
|
||||
This file contains all the settings used in production.
|
||||
|
||||
This file is required and if development.py is present these
|
||||
values are overridden.
|
||||
"""
|
||||
|
||||
from server.settings.components import config
|
||||
|
||||
# Production flags:
|
||||
# https://docs.djangoproject.com/en/2.2/howto/deployment/
|
||||
|
||||
DEBUG = False
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
# TODO: check production hosts
|
||||
config('DOMAIN_NAME'),
|
||||
|
||||
# We need this value for `healthcheck` to work:
|
||||
'localhost',
|
||||
]
|
||||
|
||||
|
||||
# Staticfiles
|
||||
# https://docs.djangoproject.com/en/2.2/ref/contrib/staticfiles/
|
||||
|
||||
# This is a hack to allow a special flag to be used with `--dry-run`
|
||||
# to test things locally.
|
||||
_COLLECTSTATIC_DRYRUN = config(
|
||||
'DJANGO_COLLECTSTATIC_DRYRUN', cast=bool, default=False,
|
||||
)
|
||||
# Adding STATIC_ROOT to collect static files via 'collectstatic':
|
||||
STATIC_ROOT = '.static' if _COLLECTSTATIC_DRYRUN else '/var/www/django/static'
|
||||
|
||||
STATICFILES_STORAGE = (
|
||||
# This is a string, not a tuple,
|
||||
# but it does not fit into 80 characters rule.
|
||||
'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
|
||||
)
|
||||
|
||||
|
||||
# Media files
|
||||
# https://docs.djangoproject.com/en/2.2/topics/files/
|
||||
|
||||
MEDIA_ROOT = '/var/www/django/media'
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
|
||||
|
||||
_PASS = 'django.contrib.auth.password_validation' # noqa: S105
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{'NAME': '{0}.UserAttributeSimilarityValidator'.format(_PASS)},
|
||||
{'NAME': '{0}.MinimumLengthValidator'.format(_PASS)},
|
||||
{'NAME': '{0}.CommonPasswordValidator'.format(_PASS)},
|
||||
{'NAME': '{0}.NumericPasswordValidator'.format(_PASS)},
|
||||
]
|
||||
|
||||
|
||||
# Security
|
||||
# https://docs.djangoproject.com/en/2.2/topics/security/
|
||||
|
||||
SECURE_HSTS_SECONDS = 31536000 # the same as Caddy has
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||
SECURE_HSTS_PRELOAD = True
|
||||
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SECURE_SSL_REDIRECT = True
|
||||
SECURE_REDIRECT_EXEMPT = [
|
||||
# This is required for healthcheck to work:
|
||||
'^health/',
|
||||
]
|
||||
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
14
new-github-repos/server/templates/txt/humans.txt
Normal file
14
new-github-repos/server/templates/txt/humans.txt
Normal file
@ -0,0 +1,14 @@
|
||||
# The humans responsible & technology colophon
|
||||
# http://humanstxt.org/
|
||||
|
||||
|
||||
## balsh
|
||||
|
||||
Team:
|
||||
|
||||
|
||||
## Technologies
|
||||
|
||||
Language: English
|
||||
Doctype: HTML5
|
||||
Technologies: Python, Django
|
2
new-github-repos/server/templates/txt/robots.txt
Normal file
2
new-github-repos/server/templates/txt/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
60
new-github-repos/server/urls.py
Normal file
60
new-github-repos/server/urls.py
Normal file
@ -0,0 +1,60 @@
|
||||
"""
|
||||
Main URL mapping configuration file.
|
||||
|
||||
Include other URLConfs from external apps using method `include()`.
|
||||
|
||||
It is also a good practice to keep a single URL to the root index page.
|
||||
|
||||
This examples uses Django's default media
|
||||
files serving technique in development.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.admindocs import urls as admindocs_urls
|
||||
from django.urls import include, path
|
||||
from django.views.generic import TemplateView
|
||||
from health_check import urls as health_urls
|
||||
|
||||
from server.apps.main import urls as main_urls
|
||||
from server.apps.main.views import index
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = [
|
||||
# Apps:
|
||||
path('main/', include(main_urls, namespace='main')),
|
||||
|
||||
# Health checks:
|
||||
path('health/', include(health_urls)), # noqa: DJ05
|
||||
|
||||
# django-admin:
|
||||
path('admin/doc/', include(admindocs_urls)), # noqa: DJ05
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
# Text and xml static files:
|
||||
path('robots.txt', TemplateView.as_view(
|
||||
template_name='txt/robots.txt',
|
||||
content_type='text/plain',
|
||||
)),
|
||||
path('humans.txt', TemplateView.as_view(
|
||||
template_name='txt/humans.txt',
|
||||
content_type='text/plain',
|
||||
)),
|
||||
|
||||
# It is a good practice to have explicit index view:
|
||||
path('', index, name='index'),
|
||||
]
|
||||
|
||||
if settings.DEBUG: # pragma: no cover
|
||||
import debug_toolbar # noqa: WPS433
|
||||
from django.conf.urls.static import static # noqa: WPS433
|
||||
|
||||
urlpatterns = [
|
||||
# URLs specific only to django-debug-toolbar:
|
||||
path('__debug__/', include(debug_toolbar.urls)), # noqa: DJ05
|
||||
] + urlpatterns + static( # type: ignore
|
||||
# Serving media files in development only:
|
||||
settings.MEDIA_URL,
|
||||
document_root=settings.MEDIA_ROOT,
|
||||
)
|
15
new-github-repos/server/wsgi.py
Normal file
15
new-github-repos/server/wsgi.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""
|
||||
WSGI config for server project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
|
||||
application = get_wsgi_application()
|
144
new-github-repos/setup.cfg
Normal file
144
new-github-repos/setup.cfg
Normal file
@ -0,0 +1,144 @@
|
||||
# All configuration for plugins and other utils is defined here.
|
||||
# Read more about `setup.cfg`:
|
||||
# https://docs.python.org/3/distutils/configfile.html
|
||||
|
||||
|
||||
[flake8]
|
||||
# flake8 configuration:
|
||||
# https://flake8.pycqa.org/en/latest/user/configuration.html
|
||||
format = wemake
|
||||
show-source = True
|
||||
statistics = False
|
||||
doctests = True
|
||||
enable-extensions = G
|
||||
|
||||
# darglint configuration:
|
||||
# https://github.com/terrencepreilly/darglint
|
||||
strictness = long
|
||||
docstring-style = numpy
|
||||
|
||||
# Flake plugins:
|
||||
max-line-length = 80
|
||||
max-complexity = 6
|
||||
|
||||
# Excluding some directories:
|
||||
exclude = .git,__pycache__,.venv,.eggs,*.egg
|
||||
|
||||
# Disable some pydocstyle checks:
|
||||
ignore = D100, D104, D106, D401, X100, W504, RST303, RST304, DAR103, DAR203
|
||||
|
||||
# Docs: https://github.com/snoack/flake8-per-file-ignores
|
||||
# You can completely or partially disable our custom checks,
|
||||
# to do so you have to ignore `WPS` letters for all python files:
|
||||
per-file-ignores =
|
||||
# Allow `__init__.py` with logic for configuration:
|
||||
server/settings/*.py: WPS226, WPS407, WPS412, WPS432
|
||||
# Allow to have magic numbers inside migrations and wrong module names:
|
||||
server/*/migrations/*.py: WPS102, WPS114, WPS432
|
||||
# Enable `assert` keyword and magic numbers for tests:
|
||||
tests/*.py: S101, WPS432
|
||||
|
||||
|
||||
[isort]
|
||||
# isort configuration:
|
||||
# https://github.com/timothycrosley/isort/wiki/isort-Settings
|
||||
include_trailing_comma = true
|
||||
use_parentheses = true
|
||||
# See https://github.com/timothycrosley/isort#multi-line-output-modes
|
||||
multi_line_output = 3
|
||||
line_length = 80
|
||||
|
||||
|
||||
[tool:pytest]
|
||||
# pytest configuration:
|
||||
# https://docs.pytest.org/en/stable/customize.html
|
||||
|
||||
# pytest-django configuration:
|
||||
# https://pytest-django.readthedocs.io/en/latest/
|
||||
DJANGO_SETTINGS_MODULE = server.settings
|
||||
|
||||
# Timeout for tests, so they can not take longer
|
||||
# than this amount of seconds.
|
||||
# You should adjust this value to be as low as possible.
|
||||
# Configuration:
|
||||
# https://pypi.org/project/pytest-timeout/
|
||||
timeout = 5
|
||||
|
||||
# Directories that are not visited by pytest collector:
|
||||
norecursedirs = *.egg .eggs dist build docs .tox .git __pycache__
|
||||
|
||||
# You will need to measure your tests speed with `-n auto` and without it,
|
||||
# so you can see whether it gives you any performance gain, or just gives
|
||||
# you an overhead. See `docs/template/development-process.rst`.
|
||||
addopts =
|
||||
--strict-markers
|
||||
--strict-config
|
||||
--doctest-modules
|
||||
--fail-on-template-vars
|
||||
--dup-fixtures
|
||||
# Output:
|
||||
--tb=short
|
||||
# Parallelism:
|
||||
# -n auto
|
||||
# --boxed
|
||||
# Coverage:
|
||||
--cov=server
|
||||
--cov=tests
|
||||
--cov-branch
|
||||
--cov-report=term-missing:skip-covered
|
||||
--cov-report=html
|
||||
--cov-fail-under=100
|
||||
|
||||
|
||||
[coverage:run]
|
||||
# Coverage configuration:
|
||||
# https://coverage.readthedocs.io/en/latest/config.html
|
||||
plugins =
|
||||
# Docs: https://github.com/nedbat/django_coverage_plugin
|
||||
django_coverage_plugin
|
||||
|
||||
|
||||
[mypy]
|
||||
# Mypy configuration:
|
||||
# https://mypy.readthedocs.io/en/latest/config_file.html
|
||||
allow_redefinition = False
|
||||
check_untyped_defs = True
|
||||
disallow_untyped_decorators = True
|
||||
disallow_any_explicit = True
|
||||
disallow_any_generics = True
|
||||
disallow_untyped_calls = True
|
||||
ignore_errors = False
|
||||
ignore_missing_imports = True
|
||||
implicit_reexport = False
|
||||
local_partial_types = True
|
||||
strict_optional = True
|
||||
strict_equality = True
|
||||
no_implicit_optional = True
|
||||
warn_unused_ignores = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_configs = True
|
||||
warn_unreachable = True
|
||||
warn_no_return = True
|
||||
|
||||
plugins =
|
||||
mypy_django_plugin.main
|
||||
|
||||
[mypy.plugins.django-stubs]
|
||||
django_settings_module = server.settings
|
||||
|
||||
[mypy-server.apps.*.migrations.*]
|
||||
# Django migrations should not produce any errors:
|
||||
ignore_errors = True
|
||||
|
||||
[mypy-server.apps.*.models]
|
||||
# FIXME: remove this line, when `django-stubs` will stop
|
||||
# using `Any` inside.
|
||||
disallow_any_explicit = False
|
||||
|
||||
|
||||
[doc8]
|
||||
# doc8 configuration:
|
||||
# https://github.com/pycqa/doc8
|
||||
ignore-path = docs/_build
|
||||
max-line-length = 80
|
||||
sphinx = True
|
45
new-github-repos/tests/conftest.py
Normal file
45
new-github-repos/tests/conftest.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""
|
||||
This module is used to provide configuration, fixtures, and plugins for pytest.
|
||||
|
||||
It may be also used for extending doctest's context:
|
||||
1. https://docs.python.org/3/library/doctest.html
|
||||
2. https://docs.pytest.org/en/latest/doctest.html
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _media_root(settings, tmpdir_factory) -> None:
|
||||
"""Forces django to save media files into temp folder."""
|
||||
settings.MEDIA_ROOT = tmpdir_factory.mktemp('media', numbered=True)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _password_hashers(settings) -> None:
|
||||
"""Forces django to use fast password hashers for tests."""
|
||||
settings.PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _auth_backends(settings) -> None:
|
||||
"""Deactivates security backend from Axes app."""
|
||||
settings.AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _debug(settings) -> None:
|
||||
"""Sets proper DEBUG and TEMPLATE debug mode for coverage."""
|
||||
settings.DEBUG = False
|
||||
for template in settings.TEMPLATES:
|
||||
template['OPTIONS']['debug'] = True
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def main_heading() -> str:
|
||||
"""An example fixture containing some html fragment."""
|
||||
return '<h1>wemake-django-template</h1>'
|
@ -0,0 +1,16 @@
|
||||
from hypothesis import given
|
||||
from hypothesis.extra import django
|
||||
|
||||
from server.apps.main.models import BlogPost
|
||||
|
||||
|
||||
class TestBlogPost(django.TestCase):
|
||||
"""This is a property-based test that ensures model correctness."""
|
||||
|
||||
@given(django.from_model(BlogPost))
|
||||
def test_model_properties(self, instance: BlogPost) -> None:
|
||||
"""Tests that instance can be saved and has correct representation."""
|
||||
instance.save()
|
||||
|
||||
assert instance.id > 0
|
||||
assert len(str(instance)) <= 20
|
@ -0,0 +1,17 @@
|
||||
import pytest
|
||||
from django_test_migrations.migrator import Migrator
|
||||
|
||||
from server.apps.main.urls import app_name
|
||||
|
||||
|
||||
def test_initial0001(migrator: Migrator) -> None:
|
||||
"""Tests the initial migration forward application."""
|
||||
old_state = migrator.apply_initial_migration((app_name, None))
|
||||
with pytest.raises(LookupError):
|
||||
# This model does not exist before this migration:
|
||||
old_state.apps.get_model(app_name, 'BlogPost')
|
||||
|
||||
new_state = migrator.apply_tested_migration((app_name, '0001_initial'))
|
||||
model = new_state.apps.get_model(app_name, 'BlogPost')
|
||||
|
||||
assert model.objects.create(title='test', body='some body')
|
@ -0,0 +1,18 @@
|
||||
from django.test import Client
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
def test_main_page(client: Client, main_heading: str) -> None:
|
||||
"""This test ensures that main page works."""
|
||||
response = client.get('/')
|
||||
|
||||
assert response.status_code == 200
|
||||
assert main_heading in str(response.content)
|
||||
|
||||
|
||||
def test_hello_page(client: Client, main_heading: str) -> None:
|
||||
"""This test ensures that hello page works."""
|
||||
response = client.get(reverse('main:hello'))
|
||||
|
||||
assert response.status_code == 200
|
||||
assert main_heading in str(response.content)
|
55
new-github-repos/tests/test_server/test_urls.py
Normal file
55
new-github-repos/tests/test_server/test_urls.py
Normal file
@ -0,0 +1,55 @@
|
||||
import pytest
|
||||
from django.test import Client
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_health_check(client: Client) -> None:
|
||||
"""This test ensures that health check is accessible."""
|
||||
response = client.get('/health/')
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_admin_unauthorized(client: Client) -> None:
|
||||
"""This test ensures that admin panel requires auth."""
|
||||
response = client.get('/admin/')
|
||||
|
||||
assert response.status_code == 302
|
||||
|
||||
|
||||
def test_admin_authorized(admin_client: Client) -> None:
|
||||
"""This test ensures that admin panel is accessible."""
|
||||
response = admin_client.get('/admin/')
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_admin_docs_unauthorized(client: Client) -> None:
|
||||
"""This test ensures that admin panel docs requires auth."""
|
||||
response = client.get('/admin/doc/')
|
||||
|
||||
assert response.status_code == 302
|
||||
|
||||
|
||||
def test_admin_docs_authorized(admin_client: Client) -> None:
|
||||
"""This test ensures that admin panel docs are accessible."""
|
||||
response = admin_client.get('/admin/doc/')
|
||||
|
||||
assert response.status_code == 200
|
||||
assert b'docutils' not in response.content
|
||||
|
||||
|
||||
def test_robots_txt(client: Client) -> None:
|
||||
"""This test ensures that `robots.txt` is accessible."""
|
||||
response = client.get('/robots.txt')
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.get('Content-Type') == 'text/plain'
|
||||
|
||||
|
||||
def test_humans_txt(client: Client) -> None:
|
||||
"""This test ensures that `humans.txt` is accessible."""
|
||||
response = client.get('/humans.txt')
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.get('Content-Type') == 'text/plain'
|
Loading…
x
Reference in New Issue
Block a user