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