mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-08-14 00:44:53 +03:00
Merge branch 'master' into allow-admin-to-use-django-allauth
# Conflicts: # {{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py
This commit is contained in:
commit
d9b1bf0779
|
@ -12,7 +12,7 @@ trim_trailing_whitespace = true
|
|||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{html,css,scss,json,yml}]
|
||||
[*.{html,css,scss,json,yml,xml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
|
56
.github/CONTRIBUTORS-template.md
vendored
Normal file
56
.github/CONTRIBUTORS-template.md
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
# Contributors
|
||||
|
||||
## Core Developers
|
||||
|
||||
These contributors have commit flags for the repository, and are able to
|
||||
accept and merge pull requests.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Github</th>
|
||||
<th>Twitter</th>
|
||||
</tr>
|
||||
{%- for contributor in core_contributors %}
|
||||
<tr>
|
||||
<td>{{ contributor.name }}</td>
|
||||
<td>
|
||||
<a href="https://github.com/{{ contributor.github_login }}">{{ contributor.github_login }}</a>
|
||||
</td>
|
||||
<td>{{ contributor.twitter_username }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
|
||||
_Audrey is also the creator of Cookiecutter. Audrey and Daniel are on
|
||||
the Cookiecutter core team._
|
||||
|
||||
## Other Contributors
|
||||
|
||||
Listed in alphabetical order.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Github</th>
|
||||
<th>Twitter</th>
|
||||
</tr>
|
||||
{%- for contributor in other_contributors %}
|
||||
<tr>
|
||||
<td>{{ contributor.name }}</td>
|
||||
<td>
|
||||
<a href="https://github.com/{{ contributor.github_login }}">{{ contributor.github_login }}</a>
|
||||
</td>
|
||||
<td>{{ contributor.twitter_username }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
|
||||
### Special Thanks
|
||||
|
||||
The following haven't provided code directly, but have provided
|
||||
guidance and advice.
|
||||
|
||||
- Jannis Leidel
|
||||
- Nate Aune
|
||||
- Barry Morrison
|
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: [pydanny, browniebroke]
|
||||
patreon: feldroy
|
||||
open_collective: cookiecutter-django
|
1
.github/ISSUE_TEMPLATE.md
vendored
1
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1 +0,0 @@
|
|||
## [Make sure to follow one of the issue templates we've got](https://github.com/pydanny/cookiecutter-django/issues/new/choose), otherwise the issue might be closed immeditely
|
59
.github/ISSUE_TEMPLATE/bug.md
vendored
59
.github/ISSUE_TEMPLATE/bug.md
vendored
|
@ -1,21 +1,64 @@
|
|||
---
|
||||
name: Bug Report
|
||||
about: Report a bug
|
||||
labels: bug
|
||||
---
|
||||
|
||||
## What happened?
|
||||
|
||||
|
||||
|
||||
|
||||
## What should've happened instead?
|
||||
|
||||
## Additional details
|
||||
|
||||
<!-- To assist you best, please include commands that you've run, options you've selected and any relevant logs -->
|
||||
|
||||
- Host system configuration:
|
||||
|
||||
## Steps to reproduce
|
||||
- Version of cookiecutter CLI (get it with `cookiecutter --version`):
|
||||
- OS name and version:
|
||||
|
||||
[//]: # (Any or all of the following:)
|
||||
[//]: # (* Host system configuration: OS, Docker & friends' versions etc.)
|
||||
[//]: # (* Replay file https://cookiecutter.readthedocs.io/en/latest/advanced/replay.html)
|
||||
[//]: # (* Logs)
|
||||
On Linux, run
|
||||
|
||||
```bash
|
||||
lsb_release -a 2> /dev/null || cat /etc/redhat-release 2> /dev/null || cat /etc/*-release 2> /dev/null || cat /etc/issue 2> /dev/null
|
||||
```
|
||||
|
||||
On MacOs, run
|
||||
|
||||
```bash
|
||||
sw_vers
|
||||
```
|
||||
|
||||
On Windows, via CMD, run
|
||||
|
||||
```
|
||||
systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
|
||||
```
|
||||
|
||||
```bash
|
||||
# Insert here the OS name and version
|
||||
|
||||
```
|
||||
|
||||
- Python version, run `python3 -V`:
|
||||
- Docker version (if using Docker), run `docker --version`:
|
||||
- docker-compose version (if using Docker), run `docker-compose --version`:
|
||||
- ...
|
||||
|
||||
- Options selected and/or [replay file](https://cookiecutter.readthedocs.io/en/latest/advanced/replay.html):
|
||||
On Linux and macOS: `cat ${HOME}/.cookiecutter_replay/cookiecutter-django.json`
|
||||
(Please, take care to remove sensitive information)
|
||||
|
||||
```json
|
||||
|
||||
```
|
||||
|
||||
<summary>
|
||||
Logs:
|
||||
<details>
|
||||
<pre>
|
||||
$ cookiecutter https://github.com/cookiecutter/cookiecutter-django
|
||||
project_name [Project Name]: ...
|
||||
</pre>
|
||||
</details>
|
||||
</summary>
|
||||
|
|
17
.github/ISSUE_TEMPLATE/feature.md
vendored
17
.github/ISSUE_TEMPLATE/feature.md
vendored
|
@ -1,24 +1,13 @@
|
|||
---
|
||||
name: New Feature Proposal
|
||||
about: Propose a new feature
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
[//]: # (What's it you're proposing? How should it be implemented?)
|
||||
|
||||
|
||||
|
||||
What are you proposing? How should it be implemented?
|
||||
|
||||
## Rationale
|
||||
|
||||
[//]: # (Why should this feature be implemented?)
|
||||
|
||||
|
||||
|
||||
|
||||
## Use case(s) / visualization(s)
|
||||
|
||||
[//]: # ("Better to see something once than to hear about it a thousand times.")
|
||||
|
||||
|
||||
Why should this feature be implemented?
|
||||
|
|
24
.github/ISSUE_TEMPLATE/improvement.md
vendored
24
.github/ISSUE_TEMPLATE/improvement.md
vendored
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
name: Improvement Suggestion
|
||||
about: Let us know how we could improve
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
[//]: # (What's it you're proposing? How should it be implemented?)
|
||||
|
||||
|
||||
|
||||
|
||||
## Rationale
|
||||
|
||||
[//]: # (Why should this feature be implemented?)
|
||||
|
||||
|
||||
|
||||
|
||||
## Use case(s) / visualization(s)
|
||||
|
||||
[//]: # ("Better to see something once than to hear about it a thousand times.")
|
||||
|
||||
|
8
.github/ISSUE_TEMPLATE/paid-support.md
vendored
8
.github/ISSUE_TEMPLATE/paid-support.md
vendored
|
@ -3,8 +3,10 @@ name: Paid Support Request
|
|||
about: Ask Core Team members to help you out
|
||||
---
|
||||
|
||||
Provided your question goes beyound [regular support](https://github.com/pydanny/cookiecutter-django/issues/new?template=question.md), and/or the task at hand is of timely/high priority nature use the below information to reach out for contributors directly.
|
||||
Provided your question goes beyond [regular support](https://github.com/cookiecutter/cookiecutter-django/issues/new?template=question.md), and/or the task at hand is of timely/high priority nature use the below information to reach out for contributors directly.
|
||||
|
||||
* Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB.
|
||||
- Bruno Alla, Core Developer ([GitHub](https://github.com/sponsors/browniebroke)).
|
||||
|
||||
* Nikita Shupeyko, Core Developer ([GitHub](https://github.com/webyneter)): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
- Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB.
|
||||
|
||||
- Nikita Shupeyko, Core Developer ([GitHub](https://github.com/webyneter)): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
|
|
9
.github/ISSUE_TEMPLATE/question.md
vendored
9
.github/ISSUE_TEMPLATE/question.md
vendored
|
@ -1,6 +1,11 @@
|
|||
---
|
||||
name: Question
|
||||
about: Please, ask your question on StackOverflow or Gitter
|
||||
about: Please ask your question on StackOverflow, Discord or GitHub Discussions.
|
||||
labels: question
|
||||
---
|
||||
|
||||
First, make sure to examine [the docs](https://cookiecutter-django.readthedocs.io/en/latest/). If that doesn't help post a question on [StackOverflow](https://stackoverflow.com/questions/tagged/cookiecutter-django) tagged with `cookiecutter-django`. Finally, feel free to join [Gitter](https://gitter.im/pydanny/cookiecutter-django) and ask around.
|
||||
First, make sure to examine [the docs](https://cookiecutter-django.readthedocs.io/en/latest/). If that doesn't help, we recommend one of these 3 main channels:
|
||||
|
||||
- If your issue is related to Django + something else but was generated with cookiecutter-django, the best is to post a question on [StackOverflow](https://stackoverflow.com/questions/tagged/cookiecutter-django) tagged with `cookiecutter-django`, you would get more visibility from other communities as well.
|
||||
- Join us on [Discord](https://discord.gg/uFXweDQc5a) and ask around.
|
||||
- Start [a discussion](https://github.com/cookiecutter/cookiecutter-django/discussions) on our project's GitHub.
|
||||
|
|
28
.github/ISSUE_TEMPLATE/regression.md
vendored
28
.github/ISSUE_TEMPLATE/regression.md
vendored
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
name: Regression Report
|
||||
about: Let us know if something that'd been working has broke
|
||||
---
|
||||
|
||||
## What happened before?
|
||||
|
||||
|
||||
|
||||
|
||||
## What happens now?
|
||||
|
||||
|
||||
|
||||
|
||||
## Last stable commit / Since when?
|
||||
|
||||
|
||||
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
[//]: # (Any or all of the following:)
|
||||
[//]: # (* Host system configuration: OS, Docker & friends' versions etc.)
|
||||
[//]: # (* Project generation options)
|
||||
[//]: # (* Logs)
|
||||
|
||||
|
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,27 +1,17 @@
|
|||
[//]: # (Thank you for helping us out: your efforts mean great deal to the project and the community as a whole!)
|
||||
|
||||
[//]: # (Before you proceed:)
|
||||
|
||||
[//]: # (1. Make sure to add yourself to `CONTRIBUTORS.rst` through this PR provided you're contributing here for the first time)
|
||||
[//]: # (2. Don't forget to update the `docs/` presuming others would benefit from a concise description of whatever that you're proposing)
|
||||
|
||||
<!-- Thank you for helping us out: your efforts mean a great deal to the project and the community as a whole! -->
|
||||
|
||||
## Description
|
||||
|
||||
[//]: # (What's it you're proposing?)
|
||||
|
||||
<!-- What's it you're proposing? -->
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] I've made sure that tests are updated accordingly (especially if adding or updating a template option)
|
||||
- [ ] I've updated the documentation or confirm that my change doesn't require any updates
|
||||
|
||||
## Rationale
|
||||
|
||||
[//]: # (Why does the project need that?)
|
||||
|
||||
|
||||
|
||||
|
||||
## Use case(s) / visualization(s)
|
||||
|
||||
[//]: # ("Better to see something once than to hear about it a thousand times.")
|
||||
|
||||
|
||||
<!--
|
||||
Why does this project need the change you're proposing?
|
||||
If this pull request fixes an open issue, don't forget to link it with `Fix #NNNN`
|
||||
-->
|
||||
|
|
11
.github/changelog-template.md
vendored
Normal file
11
.github/changelog-template.md
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
{%- for change_type, pulls in grouped_pulls.items() %}
|
||||
{%- if pulls %}
|
||||
|
||||
### {{ change_type }}
|
||||
|
||||
{%- for pull_request in pulls %}
|
||||
|
||||
- {{ pull_request.title }} ([#{{ pull_request.number }}]({{ pull_request.html_url }}))
|
||||
{%- endfor -%}
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
1402
.github/contributors.json
vendored
Normal file
1402
.github/contributors.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
88
.github/dependabot.yml
vendored
Normal file
88
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
# Config for Dependabot updates. See Documentation here:
|
||||
# https://docs.github.com/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# Update GitHub actions in workflows
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
# Update npm packages
|
||||
- package-ecosystem: "npm"
|
||||
directory: "{{cookiecutter.project_slug}}/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
# Enable version updates for Docker
|
||||
# We need to specify each Dockerfile in a separate entry because Dependabot doesn't
|
||||
# support wildcards or recursively checking subdirectories. Check this issue for updates:
|
||||
# https://github.com/dependabot/dependabot-core/issues/2178
|
||||
- package-ecosystem: "docker"
|
||||
directory: "{{cookiecutter.project_slug}}/compose/local/django/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "{{cookiecutter.project_slug}}/compose/local/docs/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "{{cookiecutter.project_slug}}/compose/local/node/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "{{cookiecutter.project_slug}}/compose/production/aws/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "{{cookiecutter.project_slug}}/compose/production/django/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "{{cookiecutter.project_slug}}/compose/production/postgres/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "{{cookiecutter.project_slug}}/compose/production/traefik/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "update"
|
115
.github/workflows/ci.yml
vendored
Normal file
115
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "main"]
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
- macOS-latest
|
||||
|
||||
name: "pytest ${{ matrix.os }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
- name: Run tests
|
||||
run: pytest -n auto tests
|
||||
|
||||
docker:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
script:
|
||||
- name: Basic
|
||||
args: "ci_tool=Gitlab"
|
||||
- name: Celery & DRF
|
||||
args: "use_celery=y use_drf=y"
|
||||
- name: Gulp
|
||||
args: "frontend_pipeline=Gulp"
|
||||
- name: Webpack
|
||||
args: "frontend_pipeline=Webpack"
|
||||
|
||||
name: "Docker ${{ matrix.script.name }}"
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
COMPOSE_DOCKER_CLI_BUILD: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
- name: Docker ${{ matrix.script.name }}
|
||||
run: sh tests/test_docker.sh ${{ matrix.script.args }}
|
||||
|
||||
bare:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
script:
|
||||
- name: Celery
|
||||
args: "use_celery=y frontend_pipeline='Django Compressor'"
|
||||
- name: Gulp
|
||||
args: "frontend_pipeline=Gulp"
|
||||
- name: Webpack
|
||||
args: "frontend_pipeline=Webpack use_heroku=y"
|
||||
- name: Email Username
|
||||
args: "username_type=email ci_tool=Github"
|
||||
|
||||
name: "Bare metal ${{ matrix.script.name }}"
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
redis:
|
||||
image: redis:5.0
|
||||
ports:
|
||||
- 6379:6379
|
||||
postgres:
|
||||
image: postgres:12
|
||||
ports:
|
||||
- 5432:5432
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
||||
env:
|
||||
CELERY_BROKER_URL: "redis://localhost:6379/0"
|
||||
# postgres://user:password@host:port/database
|
||||
DATABASE_URL: "postgres://postgres:postgres@localhost:5432/postgres"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
requirements.txt
|
||||
{{cookiecutter.project_slug}}/requirements/base.txt
|
||||
{{cookiecutter.project_slug}}/requirements/local.txt
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "18"
|
||||
- name: Bare Metal ${{ matrix.script.name }}
|
||||
run: sh tests/test_bare.sh ${{ matrix.script.args }}
|
30
.github/workflows/django-issue-checker.yml
vendored
Normal file
30
.github/workflows/django-issue-checker.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Creates a new issue for Major/Minor Django updates that keeps track
|
||||
# of all dependencies that need to be updated/merged in order for the
|
||||
# latest Django version to also be merged.
|
||||
name: Django Issue Checker
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "28 5 * * *"
|
||||
# Manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
issue-checker:
|
||||
# Disables this workflow from running in a repository that is not part of the indicated organization/user
|
||||
if: github.repository_owner == 'cookiecutter'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
- name: Create Django Major Issue
|
||||
run: python scripts/create_django_issue.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
40
.github/workflows/issue-manager.yml
vendored
Normal file
40
.github/workflows/issue-manager.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Automatically close issues or pull requests that have a label, after a custom delay, if no one replies.
|
||||
# https://github.com/tiangolo/issue-manager
|
||||
name: Issue Manager
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "12 0 * * *"
|
||||
issue_comment:
|
||||
types:
|
||||
- created
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
pull_request_target:
|
||||
types:
|
||||
- labeled
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
issue-manager:
|
||||
# Disables this workflow from running in a repository that is not part of the indicated organization/user
|
||||
if: github.repository_owner == 'cookiecutter'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tiangolo/issue-manager@0.4.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
config: >
|
||||
{
|
||||
"answered": {
|
||||
"message": "Assuming the question was answered, this will be automatically closed now."
|
||||
},
|
||||
"solved": {
|
||||
"message": "Assuming the original issue was solved, it will be automatically closed now."
|
||||
},
|
||||
"waiting": {
|
||||
"message": "Automatically closing after waiting for additional info. To re-open, please provide the additional information requested."
|
||||
}
|
||||
}
|
47
.github/workflows/pre-commit-autoupdate.yml
vendored
Normal file
47
.github/workflows/pre-commit-autoupdate.yml
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Run pre-commit autoupdate every day at midnight
|
||||
# and create a pull request if any changes
|
||||
|
||||
name: Pre-commit auto-update
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "15 2 * * *"
|
||||
workflow_dispatch: # to trigger manually
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
auto-update:
|
||||
# Disables this workflow from running in a repository that is not part of the indicated organization/user
|
||||
if: github.repository_owner == 'cookiecutter'
|
||||
permissions:
|
||||
contents: write # for peter-evans/create-pull-request to create branch
|
||||
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install pre-commit
|
||||
run: pip install pre-commit
|
||||
|
||||
- name: Autoupdate template
|
||||
run: pre-commit autoupdate
|
||||
|
||||
- name: Autoupdate generated projects
|
||||
working-directory: "{{cookiecutter.project_slug}}"
|
||||
run: pre-commit autoupdate
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: update/pre-commit-autoupdate
|
||||
title: Auto-update pre-commit hooks
|
||||
commit-message: Auto-update pre-commit hooks
|
||||
body: Update versions of tools in pre-commit configs to latest version
|
||||
labels: update
|
34
.github/workflows/update-changelog.yml
vendored
Normal file
34
.github/workflows/update-changelog.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
name: Update Changelog
|
||||
|
||||
on:
|
||||
# Every day at 2am
|
||||
schedule:
|
||||
- cron: "0 2 * * *"
|
||||
# Manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
# Disables this workflow from running in a repository that is not part of the indicated organization/user
|
||||
if: github.repository_owner == 'cookiecutter'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
- name: Set git details
|
||||
run: |
|
||||
git config --global user.name "github-actions"
|
||||
git config --global user.email "action@github.com"
|
||||
- name: Update list
|
||||
run: python scripts/update_changelog.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
39
.github/workflows/update-contributors.yml
vendored
Normal file
39
.github/workflows/update-contributors.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
name: Update Contributors
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Disables this workflow from running in a repository that is not part of the indicated organization/user
|
||||
if: github.repository_owner == 'cookiecutter'
|
||||
permissions:
|
||||
contents: write # for stefanzweifel/git-auto-commit-action to push code in repo
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
- name: Update list
|
||||
run: python scripts/update_contributors.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4.16.0
|
||||
with:
|
||||
commit_message: Update Contributors
|
||||
file_pattern: CONTRIBUTORS.md .github/contributors.json
|
12
.gitignore
vendored
12
.gitignore
vendored
|
@ -208,18 +208,6 @@ Session.vim
|
|||
tags
|
||||
|
||||
|
||||
### VirtualEnv template
|
||||
# Virtualenv
|
||||
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
|
||||
[Bb]in
|
||||
[Ii]nclude
|
||||
[Ll]ib
|
||||
[Ll]ib64
|
||||
[Ss]cripts
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
|
||||
|
||||
# Even though the project might be opened and edited
|
||||
# in any of the JetBrains IDEs, it makes no sence whatsoever
|
||||
# to 'run' anything within it since any particular cookiecutter
|
||||
|
|
50
.pre-commit-config.yaml
Normal file
50
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,50 @@
|
|||
exclude: "{{cookiecutter.project_slug}}|.github/contributors.json|CHANGELOG.md|CONTRIBUTORS.md"
|
||||
default_stages: [commit]
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: check-builtin-literals
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: "v3.0.0-alpha.9-for-vscode"
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: ["--tab-width", "2"]
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.7.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py311-plus]
|
||||
exclude: hooks/
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
|
||||
ci:
|
||||
autoupdate_schedule: weekly
|
||||
skip: []
|
||||
submodules: false
|
21
.pyup.yml
Normal file
21
.pyup.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
# configure updates globally
|
||||
# default: all
|
||||
# allowed: all, insecure, False
|
||||
update: all
|
||||
|
||||
# configure dependency pinning globally
|
||||
# default: True
|
||||
# allowed: True, False
|
||||
pin: True
|
||||
|
||||
# add a label to pull requests, default is not set
|
||||
# requires private repo permissions, even on public repos
|
||||
# default: empty
|
||||
label_prs: update
|
||||
|
||||
requirements:
|
||||
- "requirements.txt"
|
||||
- "docs/requirements.txt"
|
||||
- "{{cookiecutter.project_slug}}/requirements/base.txt"
|
||||
- "{{cookiecutter.project_slug}}/requirements/local.txt"
|
||||
- "{{cookiecutter.project_slug}}/requirements/production.txt"
|
20
.readthedocs.yaml
Normal file
20
.readthedocs.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# Declare the Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
27
.travis.yml
27
.travis.yml
|
@ -1,27 +0,0 @@
|
|||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
language: python
|
||||
|
||||
python: 3.6
|
||||
|
||||
env:
|
||||
- TOX_ENV=py36
|
||||
|
||||
before_install:
|
||||
- docker-compose -v
|
||||
- docker -v
|
||||
|
||||
script:
|
||||
- tox -e $TOX_ENV
|
||||
- sh tests/test_docker.sh
|
||||
|
||||
install:
|
||||
- pip install tox
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
3061
CHANGELOG.md
3061
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
3
CODE_OF_CONDUCT.md
Normal file
3
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## Code of Conduct
|
||||
|
||||
Everyone who interacts in the Cookiecutter project's codebase, issue trackers, chat rooms, and mailing lists is expected to follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/)
|
82
CONTRIBUTING.md
Normal file
82
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
# How to Contribute
|
||||
|
||||
Always happy to get issues identified and pull requests!
|
||||
|
||||
## General considerations
|
||||
|
||||
1. Keep it small. The smaller the change, the more likely we are to accept.
|
||||
2. Changes that fix a current issue get priority for review.
|
||||
3. Check out [GitHub guide][submit-a-pr] if you've never created a pull request before.
|
||||
|
||||
## Getting started
|
||||
|
||||
1. Fork the repo
|
||||
2. Clone your fork
|
||||
3. Create a branch for your changes
|
||||
|
||||
This last step is very important, don't start developing from master, it'll cause pain if you need to send another change later.
|
||||
|
||||
## Testing
|
||||
|
||||
You'll need to run the tests using Python 3.11. We recommend using [tox](https://tox.readthedocs.io/en/latest/) to run the tests. It will automatically create a fresh virtual environment and install our test dependencies, such as [pytest-cookies](https://pypi.python.org/pypi/pytest-cookies/) and [flake8](https://pypi.python.org/pypi/flake8/).
|
||||
|
||||
We'll also run the tests on GitHub actions when you send your pull request, but it's a good idea to run them locally before you send it.
|
||||
|
||||
### Installation
|
||||
|
||||
First, make sure that your version of Python is 3.11:
|
||||
|
||||
```bash
|
||||
$ python --version
|
||||
Python 3.11.3
|
||||
```
|
||||
|
||||
Any version that starts with 3.11 will do. If you need to install it, you can get it from [python.org](https://www.python.org/downloads/).
|
||||
|
||||
Then install `tox`, if not already installed:
|
||||
|
||||
```bash
|
||||
$ python -m pip install tox
|
||||
```
|
||||
|
||||
### Run the template's test suite
|
||||
|
||||
To run the tests of the template using the current Python version:
|
||||
|
||||
```bash
|
||||
$ tox -e py
|
||||
```
|
||||
|
||||
This uses `pytest `under the hood, and you can pass options to it after a `--`. So to run a particular test:
|
||||
|
||||
```bash
|
||||
$ tox -e py -- -k test_default_configuration
|
||||
```
|
||||
|
||||
For further information, please consult the [pytest usage docs](https://pytest.org/en/latest/how-to/usage.html#specifying-which-tests-to-run).
|
||||
|
||||
### Run the generated project tests
|
||||
|
||||
The template tests are checking that the generated project is fully rendered and that it passes `flake8`. We also have some test scripts which generate a specific project combination, install the dependencies, run the tests of the generated project, install FE dependencies and generate the docs. They will install the template dependencies, so make sure you create and activate a virtual environment first.
|
||||
|
||||
```bash
|
||||
$ python -m venv venv
|
||||
$ source venv/bin/activate
|
||||
```
|
||||
|
||||
These tests are slower and can be run with or without Docker:
|
||||
|
||||
- Without Docker: `scripts/test_bare.sh` (for bare metal)
|
||||
- With Docker: `scripts/test_docker.sh`
|
||||
|
||||
All arguments to these scripts will be passed to the `cookiecutter` CLI, letting you set options, for example:
|
||||
|
||||
```bash
|
||||
$ scripts/test_bare.sh use_celery=y
|
||||
```
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
Once you're happy with your changes and they look ok locally, push and send send [a pull request][submit-a-pr] to the main repo, which will trigger the tests on GitHub actions. If they fail, try to fix them. A maintainer should take a look at your change and give you feedback or merge it.
|
||||
|
||||
[submit-a-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request
|
|
@ -1,55 +0,0 @@
|
|||
How to Contribute
|
||||
=================
|
||||
|
||||
Always happy to get issues identified and pull requests!
|
||||
|
||||
Getting your pull request merged in
|
||||
------------------------------------
|
||||
|
||||
#. Keep it small. The smaller the pull request the more likely I'll pull it in.
|
||||
#. Pull requests that fix a current issue get priority for review.
|
||||
#. If you're not already in the `CONTRIBUTORS.rst` file, add yourself!
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
Installation
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Please install `tox`_, which is a generic virtualenv management and test command line tool.
|
||||
|
||||
`tox`_ is available for download from `PyPI`_ via `pip`_::
|
||||
|
||||
$ pip install tox
|
||||
|
||||
It will automatically create a fresh virtual environment and install our test dependencies,
|
||||
such as `pytest-cookies`_ and `flake8`_.
|
||||
|
||||
Run the Tests
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Tox uses py.test under the hood, hence it supports the same syntax for selecting tests.
|
||||
|
||||
For further information please consult the `pytest usage docs`_.
|
||||
|
||||
To run all tests using various versions of python in virtualenvs defined in tox.ini, just run tox.::
|
||||
|
||||
$ tox
|
||||
|
||||
It is possible to test with a specific version of python. To do this, the command
|
||||
is::
|
||||
|
||||
$ tox -e py36
|
||||
|
||||
This will run py.test with the python3.6 interpreter, for example.
|
||||
|
||||
To run a particular test with tox for against your current Python version::
|
||||
|
||||
$ tox -e py -- -k test_default_configuration
|
||||
|
||||
.. _`pytest usage docs`: https://pytest.org/latest/usage.html#specifying-tests-selecting-tests
|
||||
.. _`tox`: https://tox.readthedocs.io/en/latest/
|
||||
.. _`pip`: https://pypi.python.org/pypi/pip/
|
||||
.. _`pytest-cookies`: https://pypi.python.org/pypi/pytest-cookies/
|
||||
.. _`flake8`: https://pypi.python.org/pypi/flake8/
|
||||
.. _`PyPI`: https://pypi.python.org/pypi
|
1984
CONTRIBUTORS.md
Normal file
1984
CONTRIBUTORS.md
Normal file
File diff suppressed because it is too large
Load Diff
305
CONTRIBUTORS.rst
305
CONTRIBUTORS.rst
|
@ -1,305 +0,0 @@
|
|||
Contributors
|
||||
============
|
||||
|
||||
Core Developers
|
||||
---------------
|
||||
|
||||
These contributors have commit flags for the repository,
|
||||
and are able to accept and merge pull requests.
|
||||
|
||||
=========================== ================ ===========
|
||||
Name Github Twitter
|
||||
=========================== ================ ===========
|
||||
Daniel Roy Greenfeld `@pydanny`_ @pydanny
|
||||
Audrey Roy Greenfeld* `@audreyr`_ @audreyr
|
||||
Fábio C. Barrionuevo da Luz `@luzfcb`_ @luzfcb
|
||||
Saurabh Kumar `@theskumar`_ @_theskumar
|
||||
Jannis Gebauer `@jayfk`_
|
||||
Burhan Khalid `@burhan`_ @burhan
|
||||
Nikita Shupeyko `@webyneter`_ @webyneter
|
||||
Bruno Alla `@browniebroke`_ @_BrunoAlla
|
||||
Wan Liuyang `@sfdye`_ @sfdye
|
||||
=========================== ================ ===========
|
||||
|
||||
*Audrey is also the creator of Cookiecutter. Audrey and
|
||||
Daniel are on the Cookiecutter core team.*
|
||||
|
||||
.. _@pydanny: https://github.com/pydanny
|
||||
.. _@luzfcb: https://github.com/luzfcb
|
||||
.. _@theskumar: https://github.com/theskumar
|
||||
.. _@audreyr: https://github.com/audreyr
|
||||
.. _@jayfk: https://github.com/jayfk
|
||||
.. _@webyneter: https://github.com/webyneter
|
||||
.. _@browniebroke: https://github.com/browniebroke
|
||||
.. _@sfdye: https://github.com/sfdye
|
||||
|
||||
Other Contributors
|
||||
------------------
|
||||
|
||||
Listed in alphabetical order.
|
||||
|
||||
========================== ============================ ==============
|
||||
Name Github Twitter
|
||||
========================== ============================ ==============
|
||||
18 `@dezoito`_
|
||||
a7p `@a7p`_
|
||||
Aaron Eikenberry `@aeikenberry`_
|
||||
Adam Bogdał `@bogdal`_
|
||||
Adam Dobrawy `@ad-m`_
|
||||
Adam Steele `@adammsteele`
|
||||
Agam Dua
|
||||
Alberto Sanchez `@alb3rto`_
|
||||
Alex Tsai `@caffodian`_
|
||||
Alvaro [Andor] `@andor-pierdelacabeza`_
|
||||
Amjith Ramanujam `@amjith`_
|
||||
Andreas Meistad `@ameistad`_
|
||||
Andres Gonzalez `@andresgz`_
|
||||
Andrew Mikhnevich `@zcho`_
|
||||
Andy Rose
|
||||
Anna Callahan `@jazztpt`_
|
||||
Antonia Blair `@antoniablair`_ @antoniablairart
|
||||
Anuj Bansal `@ahhda`_
|
||||
Arcuri Davide `@dadokkio`_
|
||||
Areski Belaid `@areski`_
|
||||
Ashley Camba
|
||||
Barclay Gauld `@yunti`_
|
||||
Ben Warren `@bwarren2`
|
||||
Ben Lopatin
|
||||
Benjamin Abel
|
||||
Bert de Miranda `@bertdemiranda`_
|
||||
Bo Lopker `@blopker`_
|
||||
Bouke Haarsma
|
||||
Brent Payne `@brentpayne`_ @brentpayne
|
||||
Burhan Khalid `@burhan`_ @burhan
|
||||
Carl Johnson `@carlmjohnson`_ @carlmjohnson
|
||||
Catherine Devlin `@catherinedevlin`_
|
||||
Cédric Gaspoz `@cgaspoz`_
|
||||
Charlie Smith `@chuckus`_
|
||||
Chris Curvey `@ccurvey`_
|
||||
Chris Franklin
|
||||
Chris Franklin `@hairychris`_
|
||||
Chris Pappalardo `@ChrisPappalardo`_
|
||||
Christopher Clarke `@chrisdev`_
|
||||
Cole Mackenzie `@cmackenzie1`_
|
||||
Collederas `@Collederas`_
|
||||
Cristian Vargas `@cdvv7788`_
|
||||
Cullen Rhodes `@c-rhodes`_
|
||||
Dan Shultz `@shultz`_
|
||||
Daniel Hepper `@dhepper`_ @danielhepper
|
||||
Daniele Tricoli `@eriol`_
|
||||
David Díaz `@ddiazpinto`_ @DavidDiazPinto
|
||||
Davit Tovmasyan `@davitovmasyan`_
|
||||
Davur Clementsen `@dsclementsen`_ @davur
|
||||
Delio Castillo `@jangeador`_ @jangeador
|
||||
Denis Orehovsky `@apirobot`_
|
||||
Dónal Adams `@epileptic-fish`_
|
||||
Diane Chen `@purplediane`_ @purplediane88
|
||||
Dong Huynh `@trungdong`_
|
||||
Emanuel Calso `@bloodpet`_ @bloodpet
|
||||
Eraldo Energy `@eraldo`_
|
||||
Eric Groom `@ericgroom`_
|
||||
Eyad Al Sibai `@eyadsibai`_
|
||||
Felipe Arruda `@arruda`_
|
||||
Garry Cairns `@garry-cairns`_
|
||||
Garry Polley `@garrypolley`_
|
||||
Hamish Durkin `@durkode`_
|
||||
Harry Percival `@hjwp`_
|
||||
Hendrik Schneider `@hendrikschneider`_
|
||||
Henrique G. G. Pereira `@ikkebr`_
|
||||
Ian Lee `@IanLee1521`_
|
||||
Irfan Ahmad `@erfaan`_ @erfaan
|
||||
Jan Van Bruggen `@jvanbrug`_
|
||||
Jens Nilsson `@phiberjenz`_
|
||||
Jerome Leclanche `@jleclanche`_ @Adys
|
||||
Jimmy Gitonga `@afrowave`_ @afrowave
|
||||
John Cass `@jcass77`_ @cass_john
|
||||
Julien Almarcha `@sladinji`_
|
||||
Julio Castillo `@juliocc`_
|
||||
Kaido Kert `@kaidokert`_
|
||||
kappataumu `@kappataumu`_ @kappataumu
|
||||
Kaveh `@ka7eh`_
|
||||
Kevin A. Stone
|
||||
Kevin Ndung'u `@kevgathuku`_
|
||||
Keith Webber `@townie`_
|
||||
Krzysztof Szumny `@noisy`_
|
||||
Krzysztof Żuraw `@krzysztofzuraw`_
|
||||
Leonardo Jimenez `@xpostudio4`_
|
||||
Leo Zhou `@glasslion`_
|
||||
Lin Xianyi `@iynaix`_
|
||||
Luis Nell `@originell`_
|
||||
Lukas Klein
|
||||
Lyla Fischer
|
||||
Malik Sulaimanov `@flyudvik`_ @flyudvik
|
||||
Martin Blech
|
||||
Martin Saizar `@msaizar`_
|
||||
Mateusz Ostaszewski `@mostaszewski`_
|
||||
Mathijs Hoogland `@MathijsHoogland`_
|
||||
Matt Braymer-Hayes `@mattayes`_ @mattayes
|
||||
Matt Linares
|
||||
Matt Menzenski `@menzenski`_
|
||||
Matt Warren `@mfwarren`_
|
||||
Matthew Sisley `@mjsisley`_
|
||||
Meghan Heintz `@dot2dotseurat`_
|
||||
Mesut Yılmaz `@myilmaz`_
|
||||
Michael Gecht `@mimischi`_ @_mischi
|
||||
mozillazg `@mozillazg`_
|
||||
Pablo `@oubiga`_
|
||||
Parbhat Puri `@parbhat`_
|
||||
Peter Bittner `@bittner`_
|
||||
Peter Coles `@mrcoles`_
|
||||
Pierre Chiquet `@pchiquet`_
|
||||
Raphael Pierzina `@hackebrot`_
|
||||
Raony Guimarães Corrêa `@raonyguimaraes`_
|
||||
Reggie Riser `@reggieriser`_
|
||||
René Muhl `@rm--`_
|
||||
Roman Afanaskin `@siauPatrick`_
|
||||
Roman Osipenko `@romanosipenko`_
|
||||
Russell Davies
|
||||
Sascha `@saschalalala` @saschalalala
|
||||
Sam Collins `@MightySCollins`_
|
||||
Shupeyko Nikita `@webyneter`_
|
||||
Sławek Ehlert `@slafs`_
|
||||
Srinivas Nyayapati `@shireenrao`_
|
||||
stepmr `@stepmr`_
|
||||
Steve Steiner `@ssteinerX`_
|
||||
Sule Marshall `@suledev`_
|
||||
Taylor Baldwin
|
||||
Théo Segonds `@show0k`_
|
||||
Tim Freund `@timfreund`_
|
||||
Tom Atkins `@knitatoms`_
|
||||
Tom Offermann
|
||||
Travis McNeill `@Travistock`_ @tavistock_esq
|
||||
Tubo Shi `@Tubo`_
|
||||
Umair Ashraf `@umrashrf`_ @fabumair
|
||||
Vitaly Babiy
|
||||
Vivian Guillen `@viviangb`_
|
||||
Will Farley `@goldhand`_ @g01dhand
|
||||
William Archinal `@archinal`_
|
||||
Yaroslav Halchenko
|
||||
Denis Bobrov `@delneg`_
|
||||
Philipp Matthies `@canonnervio`_
|
||||
Vadim Iskuchekov `@Egregors`_ @egregors
|
||||
========================== ============================ ==============
|
||||
|
||||
.. _@a7p: https://github.com/a7p
|
||||
.. _@ad-m: https://github.com/ad-m
|
||||
.. _@adammsteele: https://github.com/adammsteele
|
||||
.. _@aeikenberry: https://github.com/aeikenberry
|
||||
.. _@alb3rto: https://github.com/alb3rto
|
||||
.. _@ameistad: https://github.com/ameistad
|
||||
.. _@amjith: https://github.com/amjith
|
||||
.. _@andor-pierdelacabeza: https://github.com/andor-pierdelacabeza
|
||||
.. _@antoniablair: https://github.com/antoniablair
|
||||
.. _@apirobot: https://github.com/apirobot
|
||||
.. _@archinal: https://github.com/archinal
|
||||
.. _@areski: https://github.com/areski
|
||||
.. _@arruda: https://github.com/arruda
|
||||
.. _@bittner: https://github.com/bittner
|
||||
.. _@bloodpet: https://github.com/bloodpet
|
||||
.. _@blopker: https://github.com/blopker
|
||||
.. _@bogdal: https://github.com/bogdal
|
||||
.. _@burhan: https://github.com/burhan
|
||||
.. _@c-rhodes: https://github.com/c-rhodes
|
||||
.. _@caffodian: https://github.com/caffodian
|
||||
.. _@carlmjohnson: https://github.com/carlmjohnson
|
||||
.. _@catherinedevlin: https://github.com/catherinedevlin
|
||||
.. _@ccurvey: https://github.com/ccurvey
|
||||
.. _@cdvv7788: https://github.com/cdvv7788
|
||||
.. _@cgaspoz: https://github.com/cgaspoz
|
||||
.. _@chrisdev: https://github.com/chrisdev
|
||||
.. _@ChrisPappalardo: https://github.com/ChrisPappalardo
|
||||
.. _@chuckus: https://github.com/chuckus
|
||||
.. _@cmackenzie1: https://github.com/cmackenzie1
|
||||
.. _@Collederas: https://github.com/Collederas
|
||||
.. _@davitovmasyan: https://github.com/davitovmasyan
|
||||
.. _@ddiazpinto: https://github.com/ddiazpinto
|
||||
.. _@dezoito: https://github.com/dezoito
|
||||
.. _@dhepper: https://github.com/dhepper
|
||||
.. _@dot2dotseurat: https://github.com/dot2dotseurat
|
||||
.. _@dsclementsen: https://github.com/dsclementsen
|
||||
.. _@durkode: https://github.com/durkode
|
||||
.. _@epileptic-fish: https://gihub.com/epileptic-fish
|
||||
.. _@eraldo: https://github.com/eraldo
|
||||
.. _@erfaan: https://github.com/erfaan
|
||||
.. _@eriol: https://github.com/eriol
|
||||
.. _@eyadsibai: https://github.com/eyadsibai
|
||||
.. _@flyudvik: https://github.com/flyudvik
|
||||
.. _@garry-cairns: https://github.com/garry-cairns
|
||||
.. _@garrypolley: https://github.com/garrypolley
|
||||
.. _@goldhand: https://github.com/goldhand
|
||||
.. _@glasslion: https://github.com/glasslion
|
||||
.. _@hackebrot: https://github.com/hackebrot
|
||||
.. _@hairychris: https://github.com/hairychris
|
||||
.. _@hendrikschneider: https://github.com/hendrikschneider
|
||||
.. _@hjwp: https://github.com/hjwp
|
||||
.. _@IanLee1521: https://github.com/IanLee1521
|
||||
.. _@ikkebr: https://github.com/ikkebr
|
||||
.. _@iynaix: https://github.com/iynaix
|
||||
.. _@jazztpt: https://github.com/jazztpt
|
||||
.. _@jleclanche: https://github.com/jleclanche
|
||||
.. _@juliocc: https://github.com/juliocc
|
||||
.. _@jvanbrug: https://github.com/jvanbrug
|
||||
.. _@ka7eh: https://github.com/ka7eh
|
||||
.. _@kaidokert: https://github.com/kaidokert
|
||||
.. _@kappataumu: https://github.com/kappataumu
|
||||
.. _@kevgathuku: https://github.com/kevgathuku
|
||||
.. _@knitatoms: https://github.com/knitatoms
|
||||
.. _@krzysztofzuraw: https://github.com/krzysztofzuraw
|
||||
.. _@msaizar: https://github.com/msaizar
|
||||
.. _@MathijsHoogland: https://github.com/MathijsHoogland
|
||||
.. _@mattayes: https://github.com/mattayes
|
||||
.. _@menzenski: https://github.com/menzenski
|
||||
.. _@mostaszewski: https://github.com/mostaszewski
|
||||
.. _@mfwarren: https://github.com/mfwarren
|
||||
.. _@mimischi: https://github.com/mimischi
|
||||
.. _@mjsisley: https://github.com/mjsisley
|
||||
.. _@myilmaz: https://github.com/myilmaz
|
||||
.. _@mozillazg: https://github.com/mozillazg
|
||||
.. _@noisy: https://github.com/noisy
|
||||
.. _@originell: https://github.com/originell
|
||||
.. _@oubiga: https://github.com/oubiga
|
||||
.. _@parbhat: https://github.com/parbhat
|
||||
.. _@raonyguimaraes: https://github.com/raonyguimaraes
|
||||
.. _@reggieriser: https://github.com/reggieriser
|
||||
.. _@rm--: https://github.com/rm--
|
||||
.. _@romanosipenko: https://github.com/romanosipenko
|
||||
.. _@shireenrao: https://github.com/shireenrao
|
||||
.. _@show0k: https://github.com/show0k
|
||||
.. _@shultz: https://github.com/shultz
|
||||
.. _@siauPatrick: https://github.com/siauPatrick
|
||||
.. _@slafs: https://github.com/slafs
|
||||
.. _@ssteinerX: https://github.com/ssteinerx
|
||||
.. _@stepmr: https://github.com/stepmr
|
||||
.. _@suledev: https://github.com/suledev
|
||||
.. _@timfreund: https://github.com/timfreund
|
||||
.. _@Travistock: https://github.com/Tavistock
|
||||
.. _@trungdong: https://github.com/trungdong
|
||||
.. _@Tubo: https://github.com/tubo
|
||||
.. _@viviangb: https://github.com/viviangb
|
||||
.. _@xpostudio4: https://github.com/xpostudio4
|
||||
.. _@yunti: https://github.com/yunti
|
||||
.. _@zcho: https://github.com/zcho
|
||||
.. _@phiberjenz: https://github.com/phiberjenz
|
||||
.. _@sladinji: https://github.com/sladinji
|
||||
.. _@andresgz: https://github.com/andresgz
|
||||
.. _@jangeador: https://github.com/jangeador
|
||||
.. _@townie: https://github.com/townie
|
||||
.. _@MightySCollins: https://github.com/MightySCollins
|
||||
.. _@dadokkio: https://github.com/dadokkio
|
||||
.. _@bwarren2: https://github.com/bwarren2
|
||||
.. _@bertdemiranda: https://github.com/bertdemiranda
|
||||
.. _@brentpayne: https://github.com/brentpayne
|
||||
.. _@afrowave: https://github.com/afrowave
|
||||
.. _@pchiquet: https://github.com/pchiquet
|
||||
.. _@delneg: https://github.com/delneg
|
||||
.. _@purplediane: https://github.com/purplediane
|
||||
.. _@umrashrf: https://github.com/umrashrf
|
||||
.. _@ahhda: https://github.com/ahhda
|
||||
Special Thanks
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The following haven't provided code directly, but have provided guidance and advice.
|
||||
|
||||
* Jannis Leidel
|
||||
* Nate Aune
|
||||
* Barry Morrison
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013-2018, Daniel Roy Greenfeld
|
||||
Copyright (c) 2013-2020, Daniel Roy Greenfeld
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
|
|
258
README.md
Normal file
258
README.md
Normal file
|
@ -0,0 +1,258 @@
|
|||
# Cookiecutter Django
|
||||
|
||||
[](https://github.com/cookiecutter/cookiecutter-django/actions/workflows/ci.yml?query=branch%3Amaster)
|
||||
[](https://cookiecutter-django.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://results.pre-commit.ci/latest/github/cookiecutter/cookiecutter-django/master)
|
||||
[](https://github.com/ambv/black)
|
||||
|
||||
[](https://pyup.io/repos/github/cookiecutter/cookiecutter-django/)
|
||||
[](https://discord.gg/uFXweDQc5a)
|
||||
[](https://www.codetriage.com/cookiecutter/cookiecutter-django)
|
||||
|
||||
Powered by [Cookiecutter](https://github.com/cookiecutter/cookiecutter), Cookiecutter Django is a framework for jumpstarting
|
||||
production-ready Django projects quickly.
|
||||
|
||||
- Documentation: <https://cookiecutter-django.readthedocs.io/en/latest/>
|
||||
- See [Troubleshooting](https://cookiecutter-django.readthedocs.io/en/latest/troubleshooting.html) for common errors and obstacles
|
||||
- If you have problems with Cookiecutter Django, please open [issues](https://github.com/cookiecutter/cookiecutter-django/issues/new) don't send
|
||||
emails to the maintainers.
|
||||
|
||||
## Features
|
||||
|
||||
- For Django 4.2
|
||||
- Works with Python 3.11
|
||||
- Renders Django projects with 100% starting test coverage
|
||||
- Twitter [Bootstrap](https://github.com/twbs/bootstrap) v5
|
||||
- [12-Factor](http://12factor.net/) based settings via [django-environ](https://github.com/joke2k/django-environ)
|
||||
- Secure by default. We believe in SSL.
|
||||
- Optimized development and production settings
|
||||
- Registration via [django-allauth](https://github.com/pennersr/django-allauth)
|
||||
- Comes with custom user model ready to go
|
||||
- Optional basic ASGI setup for Websockets
|
||||
- Optional custom static build using Gulp or Webpack
|
||||
- Send emails via [Anymail](https://github.com/anymail/django-anymail) (using [Mailgun](http://www.mailgun.com/) by default or Amazon SES if AWS is selected cloud provider, but switchable)
|
||||
- Media storage using Amazon S3, Google Cloud Storage, Azure Storage or nginx
|
||||
- Docker support using [docker-compose](https://github.com/docker/compose) for development and production (using [Traefik](https://traefik.io/) with [LetsEncrypt](https://letsencrypt.org/) support)
|
||||
- [Procfile](https://devcenter.heroku.com/articles/procfile) for deploying to Heroku
|
||||
- Instructions for deploying to [PythonAnywhere](https://www.pythonanywhere.com/)
|
||||
- Run tests with unittest or pytest
|
||||
- Customizable PostgreSQL version
|
||||
- Default integration with [pre-commit](https://github.com/pre-commit/pre-commit) for identifying simple issues before submission to code review
|
||||
|
||||
## Optional Integrations
|
||||
|
||||
_These features can be enabled during initial project setup._
|
||||
|
||||
- Serve static files from Amazon S3, Google Cloud Storage, Azure Storage or [Whitenoise](https://whitenoise.readthedocs.io/)
|
||||
- Configuration for [Celery](https://docs.celeryq.dev) and [Flower](https://github.com/mher/flower) (the latter in Docker setup only)
|
||||
- Integration with [MailHog](https://github.com/mailhog/MailHog) for local email testing
|
||||
- Integration with [Sentry](https://sentry.io/welcome/) for error logging
|
||||
|
||||
## Constraints
|
||||
|
||||
- Only maintained 3rd party libraries are used.
|
||||
- Uses PostgreSQL everywhere: 10.19 - 14.1 ([MySQL fork](https://github.com/mabdullahadeel/cookiecutter-django-mysql) also available).
|
||||
- Environment variables for configuration (This won't work with Apache/mod_wsgi).
|
||||
|
||||
## Support this Project!
|
||||
|
||||
This project is an open source project run by volunteers. You can sponsor us via [OpenCollective](https://opencollective.com/cookiecutter-django) or individually via GitHub Sponsors:
|
||||
|
||||
- Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB.
|
||||
- Fabio C. Barrionuevo, Core Developer ([GitHub](https://github.com/luzfcb)): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
- Bruno Alla, Core Developer ([GitHub](https://github.com/browniebroke)): expertise in Python/Django and DevOps.
|
||||
- Nikita Shupeyko, Core Developer ([GitHub](https://github.com/webyneter)): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
|
||||
Projects that provide financial support to the maintainers:
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.feldroy.com/products//two-scoops-of-django-3-x"><img src="https://cdn.shopify.com/s/files/1/0304/6901/products/Two-Scoops-of-Django-3-Alpha-Cover_540x_26507b15-e489-470b-8a97-02773dd498d1_1080x.jpg"></a>
|
||||
</p>
|
||||
|
||||
Two Scoops of Django 3.x is the best ice cream-themed Django reference in the universe!
|
||||
|
||||
### PyUp
|
||||
|
||||
<p align="center">
|
||||
<a href="https://pyup.io/"><img src="https://pyup.io/static/images/logo.png"></a>
|
||||
</p>
|
||||
|
||||
PyUp brings you automated security and dependency updates used by Google and other organizations. Free for open source projects!
|
||||
|
||||
## Usage
|
||||
|
||||
Let's pretend you want to create a Django project called "redditclone". Rather than using `startproject`
|
||||
and then editing the results to include your name, email, and various configuration issues that always get forgotten until the worst possible moment, get [cookiecutter](https://github.com/cookiecutter/cookiecutter) to do all the work.
|
||||
|
||||
First, get Cookiecutter. Trust me, it's awesome:
|
||||
|
||||
$ pip install "cookiecutter>=1.7.0"
|
||||
|
||||
Now run it against this repo:
|
||||
|
||||
$ cookiecutter https://github.com/cookiecutter/cookiecutter-django
|
||||
|
||||
You'll be prompted for some values. Provide them, then a Django project will be created for you.
|
||||
|
||||
**Warning**: After this point, change 'Daniel Greenfeld', 'pydanny', etc to your own information.
|
||||
|
||||
Answer the prompts with your own desired [options](http://cookiecutter-django.readthedocs.io/en/latest/project-generation-options.html). For example:
|
||||
|
||||
Cloning into 'cookiecutter-django'...
|
||||
remote: Counting objects: 550, done.
|
||||
remote: Compressing objects: 100% (310/310), done.
|
||||
remote: Total 550 (delta 283), reused 479 (delta 222)
|
||||
Receiving objects: 100% (550/550), 127.66 KiB | 58 KiB/s, done.
|
||||
Resolving deltas: 100% (283/283), done.
|
||||
project_name [My Awesome Project]: Reddit Clone
|
||||
project_slug [reddit_clone]: reddit
|
||||
description [Behold My Awesome Project!]: A reddit clone.
|
||||
author_name [Daniel Roy Greenfeld]: Daniel Greenfeld
|
||||
domain_name [example.com]: myreddit.com
|
||||
email [daniel-greenfeld@example.com]: pydanny@gmail.com
|
||||
version [0.1.0]: 0.0.1
|
||||
Select open_source_license:
|
||||
1 - MIT
|
||||
2 - BSD
|
||||
3 - GPLv3
|
||||
4 - Apache Software License 2.0
|
||||
5 - Not open source
|
||||
Choose from 1, 2, 3, 4, 5 [1]: 1
|
||||
Select username_type:
|
||||
1 - username
|
||||
2 - email
|
||||
Choose from 1, 2 [1]: 1
|
||||
timezone [UTC]: America/Los_Angeles
|
||||
windows [n]: n
|
||||
use_pycharm [n]: y
|
||||
use_docker [n]: n
|
||||
Select postgresql_version:
|
||||
1 - 14
|
||||
2 - 13
|
||||
3 - 12
|
||||
4 - 11
|
||||
5 - 10
|
||||
Choose from 1, 2, 3, 4, 5 [1]: 1
|
||||
Select cloud_provider:
|
||||
1 - AWS
|
||||
2 - GCP
|
||||
3 - None
|
||||
Choose from 1, 2, 3 [1]: 1
|
||||
Select mail_service:
|
||||
1 - Mailgun
|
||||
2 - Amazon SES
|
||||
3 - Mailjet
|
||||
4 - Mandrill
|
||||
5 - Postmark
|
||||
6 - Sendgrid
|
||||
7 - SendinBlue
|
||||
8 - SparkPost
|
||||
9 - Other SMTP
|
||||
Choose from 1, 2, 3, 4, 5, 6, 7, 8, 9 [1]: 1
|
||||
use_async [n]: n
|
||||
use_drf [n]: y
|
||||
Select frontend_pipeline:
|
||||
1 - None
|
||||
2 - Django Compressor
|
||||
3 - Gulp
|
||||
4 - Webpack
|
||||
Choose from 1, 2, 3, 4 [1]: 1
|
||||
use_celery [n]: y
|
||||
use_mailhog [n]: n
|
||||
use_sentry [n]: y
|
||||
use_whitenoise [n]: n
|
||||
use_heroku [n]: y
|
||||
Select ci_tool:
|
||||
1 - None
|
||||
2 - Travis
|
||||
3 - Gitlab
|
||||
4 - Github
|
||||
Choose from 1, 2, 3, 4 [1]: 4
|
||||
keep_local_envs_in_vcs [y]: y
|
||||
debug [n]: n
|
||||
|
||||
Enter the project and take a look around:
|
||||
|
||||
$ cd reddit/
|
||||
$ ls
|
||||
|
||||
Create a git repo and push it there:
|
||||
|
||||
$ git init
|
||||
$ git add .
|
||||
$ git commit -m "first awesome commit"
|
||||
$ git remote add origin git@github.com:pydanny/redditclone.git
|
||||
$ git push -u origin master
|
||||
|
||||
Now take a look at your repo. Don't forget to carefully look at the generated README. Awesome, right?
|
||||
|
||||
For local development, see the following:
|
||||
|
||||
- [Developing locally](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally.html)
|
||||
- [Developing locally using docker](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally-docker.html)
|
||||
|
||||
## Community
|
||||
|
||||
- Have questions? **Before you ask questions anywhere else**, please post your question on [Stack Overflow](http://stackoverflow.com/questions/tagged/cookiecutter-django) under the _cookiecutter-django_ tag. We check there periodically for questions.
|
||||
- If you think you found a bug or want to request a feature, please open an [issue](https://github.com/cookiecutter/cookiecutter-django/issues).
|
||||
- For anything else, you can chat with us on [Discord](https://discord.gg/uFXweDQc5a).
|
||||
|
||||
## For Readers of Two Scoops of Django
|
||||
|
||||
You may notice that some elements of this project do not exactly match what we describe in chapter 3. The reason for that is this project, amongst other things, serves as a test bed for trying out new ideas and concepts. Sometimes they work, sometimes they don't, but the end result is that it won't necessarily match precisely what is described in the book I co-authored.
|
||||
|
||||
## For PyUp Users
|
||||
|
||||
If you are using [PyUp](https://pyup.io) to keep your dependencies updated and secure, use the code _cookiecutter_ during checkout to get 15% off every month.
|
||||
|
||||
## "Your Stuff"
|
||||
|
||||
Scattered throughout the Python and HTML of this project are places marked with "your stuff". This is where third-party libraries are to be integrated with your project.
|
||||
|
||||
## For MySQL users
|
||||
|
||||
To get full MySQL support in addition to the default Postgresql, you can use this fork of the cookiecutter-django:
|
||||
https://github.com/mabdullahadeel/cookiecutter-django-mysql
|
||||
|
||||
## Releases
|
||||
|
||||
Need a stable release? You can find them at <https://github.com/cookiecutter/cookiecutter-django/releases>
|
||||
|
||||
## Not Exactly What You Want?
|
||||
|
||||
This is what I want. _It might not be what you want._ Don't worry, you have options:
|
||||
|
||||
### Fork This
|
||||
|
||||
If you have differences in your preferred setup, I encourage you to fork this to create your own version.
|
||||
Once you have your fork working, let me know and I'll add it to a '_Similar Cookiecutter Templates_' list here.
|
||||
It's up to you whether to rename your fork.
|
||||
|
||||
If you do rename your fork, I encourage you to submit it to the following places:
|
||||
|
||||
- [cookiecutter](https://github.com/cookiecutter/cookiecutter) so it gets listed in the README as a template.
|
||||
- The cookiecutter [grid](https://www.djangopackages.com/grids/g/cookiecutters/) on Django Packages.
|
||||
|
||||
### Submit a Pull Request
|
||||
|
||||
We accept pull requests if they're small, atomic, and make our own project development
|
||||
experience better.
|
||||
|
||||
## Articles
|
||||
|
||||
- [Cookiecutter Django With Amazon RDS](https://haseeburrehman.com/posts/cookiecutter-django-with-amazon-rds/) - Apr, 2, 2021
|
||||
- [Complete Walkthrough: Blue/Green Deployment to AWS ECS using GitHub actions](https://github.com/Andrew-Chen-Wang/cookiecutter-django-ecs-github) - June 10, 2020
|
||||
- [Using cookiecutter-django with Google Cloud Storage](https://ahhda.github.io/cloud/gce/django/2019/03/12/using-django-cookiecutter-cloud-storage.html) - Mar. 12, 2019
|
||||
- [cookiecutter-django with Nginx, Route 53 and ELB](https://msaizar.com/blog/cookiecutter-django-nginx-route-53-and-elb/) - Feb. 12, 2018
|
||||
- [cookiecutter-django and Amazon RDS](https://msaizar.com/blog/cookiecutter-django-and-amazon-rds/) - Feb. 7, 2018
|
||||
- [Using Cookiecutter to Jumpstart a Django Project on Windows with PyCharm](https://joshuahunter.com/posts/using-cookiecutter-to-jumpstart-a-django-project-on-windows-with-pycharm/) - May 19, 2017
|
||||
- [Exploring with Cookiecutter](http://www.snowboardingcoder.com/django/2016/12/03/exploring-with-cookiecutter/) - Dec. 3, 2016
|
||||
- [Introduction to Cookiecutter-Django](http://krzysztofzuraw.com/blog/2016/django-cookiecutter.html) - Feb. 19, 2016
|
||||
- [Django and GitLab - Running Continuous Integration and tests with your FREE account](http://dezoito.github.io/2016/05/11/django-gitlab-continuous-integration-phantomjs.html) - May. 11, 2016
|
||||
- [Development and Deployment of Cookiecutter-Django on Fedora](https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-on-fedora/) - Jan. 18, 2016
|
||||
- [Development and Deployment of Cookiecutter-Django via Docker](https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-via-docker/) - Dec. 29, 2015
|
||||
- [How to create a Django Application using Cookiecutter and Django 1.8](https://www.swapps.io/blog/how-to-create-a-django-application-using-cookiecutter-and-django-1-8/) - Sept. 12, 2015
|
||||
|
||||
Have a blog or online publication? Write about your cookiecutter-django tips and tricks, then send us a pull request with the link.
|
314
README.rst
314
README.rst
|
@ -1,314 +0,0 @@
|
|||
Cookiecutter Django
|
||||
=======================
|
||||
|
||||
.. image:: https://travis-ci.org/pydanny/cookiecutter-django.svg?branch=master
|
||||
:target: https://travis-ci.org/pydanny/cookiecutter-django?branch=master
|
||||
:alt: Build Status
|
||||
|
||||
.. image:: https://pyup.io/repos/github/pydanny/cookiecutter-django/shield.svg
|
||||
:target: https://pyup.io/repos/github/pydanny/cookiecutter-django/
|
||||
:alt: Updates
|
||||
|
||||
.. image:: https://badges.gitter.im/Join Chat.svg
|
||||
:target: https://gitter.im/pydanny/cookiecutter-django?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
.. image:: https://www.codetriage.com/pydanny/cookiecutter-django/badges/users.svg
|
||||
:target: https://www.codetriage.com/pydanny/cookiecutter-django
|
||||
:alt: Code Helpers Badge
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/ambv/black
|
||||
:alt: Code style: black
|
||||
|
||||
Powered by Cookiecutter_, Cookiecutter Django is a framework for jumpstarting
|
||||
production-ready Django projects quickly.
|
||||
|
||||
* Documentation: https://cookiecutter-django.readthedocs.io/en/latest/
|
||||
* See Troubleshooting_ for common errors and obstacles
|
||||
* If you have problems with Cookiecutter Django, please open issues_ don't send
|
||||
emails to the maintainers.
|
||||
|
||||
.. _Troubleshooting: https://cookiecutter-django.readthedocs.io/en/latest/troubleshooting.html
|
||||
|
||||
.. _528: https://github.com/pydanny/cookiecutter-django/issues/528#issuecomment-212650373
|
||||
.. _issues: https://github.com/pydanny/cookiecutter-django/issues/new
|
||||
|
||||
Features
|
||||
---------
|
||||
|
||||
* For Django 2.0
|
||||
* Works with Python 3.6
|
||||
* Renders Django projects with 100% starting test coverage
|
||||
* Twitter Bootstrap_ v4.1.1 (`maintained Foundation fork`_ also available)
|
||||
* 12-Factor_ based settings via django-environ_
|
||||
* Secure by default. We believe in SSL.
|
||||
* Optimized development and production settings
|
||||
* Registration via django-allauth_
|
||||
* Comes with custom user model ready to go
|
||||
* Optional custom static build using Gulp and livereload
|
||||
* Send emails via Anymail_ (using Mailgun_ by default, but switchable)
|
||||
* Media storage using Amazon S3
|
||||
* Docker support using docker-compose_ for development and production (using Caddy_ with LetsEncrypt_ support)
|
||||
* Procfile_ for deploying to Heroku
|
||||
* Instructions for deploying to PythonAnywhere_
|
||||
* Run tests with unittest or py.test
|
||||
* Customizable PostgreSQL version
|
||||
|
||||
.. _`maintained Foundation fork`: https://github.com/Parbhat/cookiecutter-django-foundation
|
||||
|
||||
|
||||
Optional Integrations
|
||||
---------------------
|
||||
|
||||
*These features can be enabled during initial project setup.*
|
||||
|
||||
* Serve static files from Amazon S3 or Whitenoise_
|
||||
* Configuration for Celery_ and Flower_ (the latter in Docker setup only)
|
||||
* Integration with MailHog_ for local email testing
|
||||
* Integration with Sentry_ for error logging
|
||||
|
||||
.. _Bootstrap: https://github.com/twbs/bootstrap
|
||||
.. _django-environ: https://github.com/joke2k/django-environ
|
||||
.. _12-Factor: http://12factor.net/
|
||||
.. _django-allauth: https://github.com/pennersr/django-allauth
|
||||
.. _django-avatar: https://github.com/grantmcconnaughey/django-avatar
|
||||
.. _Procfile: https://devcenter.heroku.com/articles/procfile
|
||||
.. _Mailgun: http://www.mailgun.com/
|
||||
.. _Whitenoise: https://whitenoise.readthedocs.io/
|
||||
.. _Celery: http://www.celeryproject.org/
|
||||
.. _Flower: https://github.com/mher/flower
|
||||
.. _Anymail: https://github.com/anymail/django-anymail
|
||||
.. _MailHog: https://github.com/mailhog/MailHog
|
||||
.. _Sentry: https://sentry.io/welcome/
|
||||
.. _docker-compose: https://github.com/docker/compose
|
||||
.. _PythonAnywhere: https://www.pythonanywhere.com/
|
||||
.. _Caddy: https://caddyserver.com/
|
||||
.. _LetsEncrypt: https://letsencrypt.org/
|
||||
|
||||
Constraints
|
||||
-----------
|
||||
|
||||
* Only maintained 3rd party libraries are used.
|
||||
* Uses PostgreSQL everywhere (9.2+)
|
||||
* Environment variables for configuration (This won't work with Apache/mod_wsgi except on AWS ELB).
|
||||
|
||||
Support this Project!
|
||||
----------------------
|
||||
|
||||
This project is run by volunteers. Please support them in their efforts to maintain and improve Cookiecutter Django:
|
||||
|
||||
* Daniel Roy Greenfeld, Project Lead (`GitHub <https://github.com/pydanny>`_, `Patreon <https://www.patreon.com/danielroygreenfeld>`_): expertise in Django and AWS ELB.
|
||||
|
||||
* Nikita Shupeyko, Core Developer (`GitHub <https://github.com/webyneter>`_): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
|
||||
Projects that provide financial support to the maintainers:
|
||||
|
||||
Two Scoops of Django 1.11
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. image:: https://cdn.shopify.com/s/files/1/0304/6901/products/tsd-111-alpha_medium.jpg?v=1499531513
|
||||
:name: Two Scoops of Django 1.11 Cover
|
||||
:align: center
|
||||
:alt: Two Scoops of Django
|
||||
:target: http://twoscoopspress.com/products/two-scoops-of-django-1-11
|
||||
|
||||
Two Scoops of Django is the best dessert-themed Django reference in the universe
|
||||
|
||||
pyup
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. image:: https://pyup.io/static/images/logo.png
|
||||
:name: pyup
|
||||
:align: center
|
||||
:alt: pyup
|
||||
:target: https://pyup.io/
|
||||
|
||||
Pyup brings you automated security and dependency updates used by Google and other organizations. Free for open source projects!
|
||||
|
||||
Usage
|
||||
------
|
||||
|
||||
Let's pretend you want to create a Django project called "redditclone". Rather than using ``startproject``
|
||||
and then editing the results to include your name, email, and various configuration issues that always get forgotten until the worst possible moment, get cookiecutter_ to do all the work.
|
||||
|
||||
First, get Cookiecutter. Trust me, it's awesome::
|
||||
|
||||
$ pip install "cookiecutter>=1.4.0"
|
||||
|
||||
Now run it against this repo::
|
||||
|
||||
$ cookiecutter https://github.com/pydanny/cookiecutter-django
|
||||
|
||||
You'll be prompted for some values. Provide them, then a Django project will be created for you.
|
||||
|
||||
**Warning**: After this point, change 'Daniel Greenfeld', 'pydanny', etc to your own information.
|
||||
|
||||
Answer the prompts with your own desired options_. For example::
|
||||
|
||||
Cloning into 'cookiecutter-django'...
|
||||
remote: Counting objects: 550, done.
|
||||
remote: Compressing objects: 100% (310/310), done.
|
||||
remote: Total 550 (delta 283), reused 479 (delta 222)
|
||||
Receiving objects: 100% (550/550), 127.66 KiB | 58 KiB/s, done.
|
||||
Resolving deltas: 100% (283/283), done.
|
||||
project_name [Project Name]: Reddit Clone
|
||||
project_slug [reddit_clone]: reddit
|
||||
author_name [Daniel Roy Greenfeld]: Daniel Greenfeld
|
||||
email [you@example.com]: pydanny@gmail.com
|
||||
description [A short description of the project.]: A reddit clone.
|
||||
domain_name [example.com]: myreddit.com
|
||||
version [0.1.0]: 0.0.1
|
||||
timezone [UTC]: America/Los_Angeles
|
||||
use_whitenoise [n]: n
|
||||
use_celery [n]: y
|
||||
use_mailhog [n]: n
|
||||
use_sentry [n]: y
|
||||
use_pycharm [n]: y
|
||||
windows [n]: n
|
||||
use_docker [n]: n
|
||||
use_heroku [n]: y
|
||||
use_compressor [n]: y
|
||||
Select postgresql_version:
|
||||
1 - 10.3
|
||||
2 - 10.2
|
||||
3 - 10.1
|
||||
4 - 9.6
|
||||
5 - 9.5
|
||||
6 - 9.4
|
||||
7 - 9.3
|
||||
Choose from 1, 2, 3, 4 [1]: 1
|
||||
Select js_task_runner:
|
||||
1 - None
|
||||
2 - Gulp
|
||||
Choose from 1, 2 [1]: 1
|
||||
custom_bootstrap_compilation [n]: n
|
||||
Select open_source_license:
|
||||
1 - MIT
|
||||
2 - BSD
|
||||
3 - GPLv3
|
||||
4 - Apache Software License 2.0
|
||||
5 - Not open source
|
||||
Choose from 1, 2, 3, 4, 5 [1]: 1
|
||||
keep_local_envs_in_vcs [y]: y
|
||||
debug[n]: n
|
||||
|
||||
Enter the project and take a look around::
|
||||
|
||||
$ cd reddit/
|
||||
$ ls
|
||||
|
||||
Create a git repo and push it there::
|
||||
|
||||
$ git init
|
||||
$ git add .
|
||||
$ git commit -m "first awesome commit"
|
||||
$ git remote add origin git@github.com:pydanny/redditclone.git
|
||||
$ git push -u origin master
|
||||
|
||||
Now take a look at your repo. Don't forget to carefully look at the generated README. Awesome, right?
|
||||
|
||||
For local development, see the following:
|
||||
|
||||
* `Developing locally`_
|
||||
* `Developing locally using docker`_
|
||||
|
||||
.. _options: http://cookiecutter-django.readthedocs.io/en/latest/project-generation-options.html
|
||||
.. _`Developing locally`: http://cookiecutter-django.readthedocs.io/en/latest/developing-locally.html
|
||||
.. _`Developing locally using docker`: http://cookiecutter-django.readthedocs.io/en/latest/developing-locally-docker.html
|
||||
|
||||
Community
|
||||
-----------
|
||||
|
||||
* Have questions? **Before you ask questions anywhere else**, please post your question on `Stack Overflow`_ under the *cookiecutter-django* tag. We check there periodically for questions.
|
||||
* If you think you found a bug or want to request a feature, please open an issue_.
|
||||
* For anything else, you can chat with us on `Gitter`_.
|
||||
|
||||
.. _`Stack Overflow`: http://stackoverflow.com/questions/tagged/cookiecutter-django
|
||||
.. _`issue`: https://github.com/pydanny/cookiecutter-django/issues
|
||||
.. _`Gitter`: https://gitter.im/pydanny/cookiecutter-django?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
For Readers of Two Scoops of Django
|
||||
--------------------------------------------
|
||||
|
||||
You may notice that some elements of this project do not exactly match what we describe in chapter 3. The reason for that is this project, amongst other things, serves as a test bed for trying out new ideas and concepts. Sometimes they work, sometimes they don't, but the end result is that it won't necessarily match precisely what is described in the book I co-authored.
|
||||
|
||||
For pyup.io Users
|
||||
-----------------
|
||||
|
||||
If you are using `pyup.io`_ to keep your dependencies updated and secure, use the code *cookiecutter* during checkout to get 15% off every month.
|
||||
|
||||
.. _`pyup.io`: https://pyup.io
|
||||
|
||||
"Your Stuff"
|
||||
-------------
|
||||
|
||||
Scattered throughout the Python and HTML of this project are places marked with "your stuff". This is where third-party libraries are to be integrated with your project.
|
||||
|
||||
Releases
|
||||
--------
|
||||
|
||||
Need a stable release? You can find them at https://github.com/pydanny/cookiecutter-django/releases
|
||||
|
||||
|
||||
Not Exactly What You Want?
|
||||
---------------------------
|
||||
|
||||
This is what I want. *It might not be what you want.* Don't worry, you have options:
|
||||
|
||||
Fork This
|
||||
~~~~~~~~~~
|
||||
|
||||
If you have differences in your preferred setup, I encourage you to fork this to create your own version.
|
||||
Once you have your fork working, let me know and I'll add it to a '*Similar Cookiecutter Templates*' list here.
|
||||
It's up to you whether or not to rename your fork.
|
||||
|
||||
If you do rename your fork, I encourage you to submit it to the following places:
|
||||
|
||||
* cookiecutter_ so it gets listed in the README as a template.
|
||||
* The cookiecutter grid_ on Django Packages.
|
||||
|
||||
.. _cookiecutter: https://github.com/audreyr/cookiecutter
|
||||
.. _grid: https://www.djangopackages.com/grids/g/cookiecutters/
|
||||
|
||||
Submit a Pull Request
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We accept pull requests if they're small, atomic, and make our own project development
|
||||
experience better.
|
||||
|
||||
Articles
|
||||
---------
|
||||
|
||||
* `cookiecutter-django with Nginx, Route 53 and ELB`_ - Feb. 12, 2018
|
||||
* `cookiecutter-django and Amazon RDS`_ - Feb. 7, 2018
|
||||
* `Deploying Cookiecutter-Django with Docker-Compose`_ - Oct. 19, 2017
|
||||
* `Using Cookiecutter to Jumpstart a Django Project on Windows with PyCharm`_ - May 19, 2017
|
||||
* `Exploring with Cookiecutter`_ - Dec. 3, 2016
|
||||
* `Introduction to Cookiecutter-Django`_ - Feb. 19, 2016
|
||||
* `Django and GitLab - Running Continuous Integration and tests with your FREE account`_ - May. 11, 2016
|
||||
* `Development and Deployment of Cookiecutter-Django on Fedora`_ - Jan. 18, 2016
|
||||
* `Development and Deployment of Cookiecutter-Django via Docker`_ - Dec. 29, 2015
|
||||
* `How to create a Django Application using Cookiecutter and Django 1.8`_ - Sept. 12, 2015
|
||||
|
||||
Have a blog or online publication? Write about your cookiecutter-django tips and tricks, then send us a pull request with the link.
|
||||
|
||||
.. _`cookiecutter-django with Nginx, Route 53 and ELB`: https://msaizar.com/blog/cookiecutter-django-nginx-route-53-and-elb/
|
||||
.. _`cookiecutter-django and Amazon RDS`: https://msaizar.com/blog/cookiecutter-django-and-amazon-rds/
|
||||
.. _`Deploying Cookiecutter-Django with Docker-Compose`: http://adamantine.me/2017/10/19/deploying-cookiecutter-django-with-docker-compose/
|
||||
.. _`Exploring with Cookiecutter`: http://www.snowboardingcoder.com/django/2016/12/03/exploring-with-cookiecutter/
|
||||
.. _`Using Cookiecutter to Jumpstart a Django Project on Windows with PyCharm`: https://joshuahunter.com/posts/using-cookiecutter-to-jumpstart-a-django-project-on-windows-with-pycharm/
|
||||
|
||||
.. _`Development and Deployment of Cookiecutter-Django via Docker`: https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-via-docker/
|
||||
.. _`Development and Deployment of Cookiecutter-Django on Fedora`: https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-on-fedora/
|
||||
.. _`How to create a Django Application using Cookiecutter and Django 1.8`: https://www.swapps.io/blog/how-to-create-a-django-application-using-cookiecutter-and-django-1-8/
|
||||
.. _`Introduction to Cookiecutter-Django`: http://krzysztofzuraw.com/blog/2016/django-cookiecutter.html
|
||||
.. _`Django and GitLab - Running Continuous Integration and tests with your FREE account`: http://dezoito.github.io/2016/05/11/django-gitlab-continuous-integration-phantomjs.html
|
||||
|
||||
Code of Conduct
|
||||
---------------
|
||||
|
||||
Everyone interacting in the Cookiecutter project's codebases, issue trackers, chat
|
||||
rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_.
|
||||
|
||||
|
||||
.. _`PyPA Code of Conduct`: https://www.pypa.io/en/latest/code-of-conduct/
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Behold My Awesome Project!",
|
||||
"author_name": "Daniel Roy Greenfeld",
|
||||
"domain_name": "example.com",
|
||||
"email": "{{ cookiecutter.author_name.lower()|replace(' ', '-') }}@example.com",
|
||||
"email": "{{ cookiecutter.author_name.lower() | trim() |replace(' ', '-') }}@{{ cookiecutter.domain_name.lower() | trim() }}",
|
||||
"version": "0.1.0",
|
||||
"open_source_license": [
|
||||
"MIT",
|
||||
|
@ -13,34 +13,33 @@
|
|||
"Apache Software License 2.0",
|
||||
"Not open source"
|
||||
],
|
||||
"username_type": ["username", "email"],
|
||||
"timezone": "UTC",
|
||||
"windows": "n",
|
||||
"use_pycharm": "n",
|
||||
"use_docker": "n",
|
||||
"postgresql_version": [
|
||||
"10.5",
|
||||
"10.4",
|
||||
"10.3",
|
||||
"10.2",
|
||||
"10.1",
|
||||
"9.6",
|
||||
"9.5",
|
||||
"9.4",
|
||||
"9.3"
|
||||
"postgresql_version": ["14", "13", "12", "11", "10"],
|
||||
"cloud_provider": ["AWS", "GCP", "Azure", "None"],
|
||||
"mail_service": [
|
||||
"Mailgun",
|
||||
"Amazon SES",
|
||||
"Mailjet",
|
||||
"Mandrill",
|
||||
"Postmark",
|
||||
"Sendgrid",
|
||||
"SendinBlue",
|
||||
"SparkPost",
|
||||
"Other SMTP"
|
||||
],
|
||||
"js_task_runner": [
|
||||
"None",
|
||||
"Gulp"
|
||||
],
|
||||
"custom_bootstrap_compilation": "n",
|
||||
"use_compressor": "n",
|
||||
"use_async": "n",
|
||||
"use_drf": "n",
|
||||
"frontend_pipeline": ["None", "Django Compressor", "Gulp", "Webpack"],
|
||||
"use_celery": "n",
|
||||
"use_mailhog": "n",
|
||||
"use_sentry": "n",
|
||||
"use_whitenoise": "n",
|
||||
"use_heroku": "n",
|
||||
"use_travisci": "n",
|
||||
"ci_tool": ["None", "Travis", "Gitlab", "Github"],
|
||||
"keep_local_envs_in_vcs": "y",
|
||||
|
||||
"debug": "n"
|
||||
}
|
||||
|
|
13
docs/conf.py
13
docs/conf.py
|
@ -7,10 +7,7 @@
|
|||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
from datetime import datetime
|
||||
import os
|
||||
import sys
|
||||
|
||||
now = datetime.now()
|
||||
|
||||
|
@ -26,13 +23,13 @@ now = datetime.now()
|
|||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = []
|
||||
extensions = ["myst_parser"]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = ".rst"
|
||||
source_suffix = [".rst", ".md"]
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
@ -42,7 +39,7 @@ master_doc = "index"
|
|||
|
||||
# General information about the project.
|
||||
project = "Cookiecutter Django"
|
||||
copyright = "2013-2018, Daniel Roy Greenfeld".format(now.year)
|
||||
copyright = f"2013-{now.year}, Daniel Roy Greenfeld"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
@ -92,7 +89,7 @@ pygments_style = "sphinx"
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = "default"
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# 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
|
||||
|
@ -242,7 +239,7 @@ texinfo_documents = [
|
|||
"Cookiecutter Django documentation",
|
||||
"Daniel Roy Greenfeld",
|
||||
"Cookiecutter Django",
|
||||
"A Cookiecutter template for creating production-ready Django projects quickly.",
|
||||
"A Cookiecutter template for creating production-ready " "Django projects quickly.",
|
||||
"Miscellaneous",
|
||||
)
|
||||
]
|
||||
|
|
3
docs/contributing.md
Normal file
3
docs/contributing.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
```{include} ../CONTRIBUTING.md
|
||||
|
||||
```
|
|
@ -3,25 +3,26 @@ Deployment on Heroku
|
|||
|
||||
.. index:: Heroku
|
||||
|
||||
Script
|
||||
------
|
||||
|
||||
Run these commands to deploy the project to Heroku:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku create --buildpack https://github.com/heroku/heroku-buildpack-python
|
||||
heroku create --buildpack heroku/python
|
||||
|
||||
heroku addons:create heroku-postgresql:hobby-dev
|
||||
heroku addons:create heroku-postgresql:mini
|
||||
# On Windows use double quotes for the time zone, e.g.
|
||||
# heroku pg:backups schedule --at "02:00 America/Los_Angeles" DATABASE_URL
|
||||
heroku pg:backups schedule --at '02:00 America/Los_Angeles' DATABASE_URL
|
||||
heroku pg:promote DATABASE_URL
|
||||
|
||||
heroku addons:create heroku-redis:hobby-dev
|
||||
heroku addons:create heroku-redis:mini
|
||||
|
||||
# If using mailgun:
|
||||
# Assuming you chose Mailgun as mail service (see below for others)
|
||||
heroku addons:create mailgun:starter
|
||||
|
||||
heroku addons:create sentry:f1
|
||||
|
||||
heroku config:set PYTHONHASHSEED=random
|
||||
|
||||
heroku config:set WEB_CONCURRENCY=4
|
||||
|
@ -30,7 +31,7 @@ Run these commands to deploy the project to Heroku:
|
|||
heroku config:set DJANGO_SETTINGS_MODULE=config.settings.production
|
||||
heroku config:set DJANGO_SECRET_KEY="$(openssl rand -base64 64)"
|
||||
|
||||
# Generating a 32 character-long random string without any of the visually similiar characters "IOl01":
|
||||
# Generating a 32 character-long random string without any of the visually similar characters "IOl01":
|
||||
heroku config:set DJANGO_ADMIN_URL="$(openssl rand -base64 4096 | tr -dc 'A-HJ-NP-Za-km-z2-9' | head -c 32)/"
|
||||
|
||||
# Set this to your Heroku app url, e.g. 'bionic-beaver-28392.herokuapp.com'
|
||||
|
@ -47,10 +48,81 @@ Run these commands to deploy the project to Heroku:
|
|||
|
||||
git push heroku master
|
||||
|
||||
heroku run python manage.py migrate
|
||||
heroku run python manage.py createsuperuser
|
||||
heroku run python manage.py collectstatic --no-input
|
||||
|
||||
heroku run python manage.py check --deploy
|
||||
|
||||
heroku open
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Email Service
|
||||
+++++++++++++
|
||||
|
||||
The script above assumes that you've chose Mailgun as email service. If you want to use another one, check the `documentation for django-anymail <https://anymail.readthedocs.io>`_ to know which environment variables to set. Heroku provides other `add-ons for emails <https://elements.heroku.com/addons#email-sms>`_ (e.g. Sendgrid) which can be configured with a similar one line command.
|
||||
|
||||
.. warning::
|
||||
|
||||
.. include:: mailgun.rst
|
||||
|
||||
Heroku & Docker
|
||||
+++++++++++++++
|
||||
|
||||
Although Heroku has some sort of `Docker support`_, it's not supported by cookiecutter-django.
|
||||
We invite you to follow Heroku documentation about it.
|
||||
|
||||
.. _Docker support: https://devcenter.heroku.com/articles/build-docker-images-heroku-yml
|
||||
|
||||
Optional actions
|
||||
----------------
|
||||
|
||||
Celery
|
||||
++++++
|
||||
|
||||
Celery requires a few extra environment variables to be ready operational. Also, the worker is created,
|
||||
it's in the ``Procfile``, but is turned off by default:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Set the broker URL to Redis
|
||||
heroku config:set CELERY_BROKER_URL=`heroku config:get REDIS_URL`
|
||||
# Scale dyno to 1 instance
|
||||
heroku ps:scale worker=1
|
||||
|
||||
Sentry
|
||||
++++++
|
||||
|
||||
If you're opted for Sentry error tracking, you can either install it through the `Sentry add-on`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku addons:create sentry:f1
|
||||
|
||||
|
||||
Or add the DSN for your account, if you already have one:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku config:set SENTRY_DSN=https://xxxx@sentry.io/12345
|
||||
|
||||
.. _Sentry add-on: https://elements.heroku.com/addons/sentry
|
||||
|
||||
|
||||
Gulp or Webpack
|
||||
+++++++++++++++
|
||||
|
||||
If you've opted for Gulp or Webpack as frontend pipeline, you'll most likely need to setup
|
||||
your app to use `multiple buildpacks`_: one for Python & one for Node.js:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku buildpacks:add --index 1 heroku/nodejs
|
||||
|
||||
At time of writing, this should do the trick: during deployment,
|
||||
the Heroku should run ``npm install`` and then ``npm build``,
|
||||
which run the SASS compilation & JS bundling.
|
||||
|
||||
If things don't work, please refer to the Heroku docs.
|
||||
|
||||
.. _multiple buildpacks: https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app
|
||||
|
|
|
@ -15,7 +15,7 @@ Full instructions follow, but here's a high-level view.
|
|||
|
||||
2. Set your config variables in the *postactivate* script
|
||||
|
||||
3. Run the *manage.py* ``migrate`` and ``collectstatic`` commands
|
||||
3. Run the *manage.py* ``migrate`` and ``collectstatic`` commands. If you've opted for django-compressor, also run ``compress``
|
||||
|
||||
4. Add an entry to the PythonAnywhere *Web tab*
|
||||
|
||||
|
@ -25,20 +25,19 @@ Full instructions follow, but here's a high-level view.
|
|||
Once you've been through this one-off config, future deployments are much simpler: just ``git pull`` and then hit the "Reload" button :)
|
||||
|
||||
|
||||
|
||||
Getting your code and dependencies installed on PythonAnywhere
|
||||
--------------------------------------------------------------
|
||||
|
||||
Make sure your project is fully commited and pushed up to Bitbucket or Github or wherever it may be. Then, log into your PythonAnywhere account, open up a **Bash** console, clone your repo, and create a virtualenv:
|
||||
Make sure your project is fully committed and pushed up to Bitbucket or Github or wherever it may be. Then, log into your PythonAnywhere account, open up a **Bash** console, clone your repo, and create a virtualenv:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone <my-repo-url> # you can also use hg
|
||||
cd my-project-name
|
||||
mkvirtualenv --python=/usr/bin/python3.6 my-project-name
|
||||
mkvirtualenv --python=/usr/bin/python3.10 my-project-name
|
||||
pip install -r requirements/production.txt # may take a few minutes
|
||||
|
||||
|
||||
.. note:: We're creating the virtualenv using Python 3.10 (``--python=/usr/bin/python3.10```), although Cookiecutter Django generates a project for Python 3.11. This is because, at time of writing, PythonAnywhere only supports Python 3.10. It shouldn't be a problem, but if is, you may try changing the Python version to 3.11 and see if it works. If it does, please let us know, or even better, submit a pull request to update this section.
|
||||
|
||||
Setting environment variables in the console
|
||||
--------------------------------------------
|
||||
|
@ -57,7 +56,7 @@ Set environment variables via the virtualenv "postactivate" script (this will se
|
|||
|
||||
vi $VIRTUAL_ENV/bin/postactivate
|
||||
|
||||
**TIP:** *If you don't like vi, you can also edit this file via the PythonAnywhere "Files" menu; look in the ".virtualenvs" folder*.
|
||||
.. note:: If you don't like vi, you can also edit this file via the PythonAnywhere "Files" menu; look in the ".virtualenvs" folder.
|
||||
|
||||
Add these exports
|
||||
|
||||
|
@ -73,13 +72,14 @@ Add these exports
|
|||
export DJANGO_AWS_ACCESS_KEY_ID=
|
||||
export DJANGO_AWS_SECRET_ACCESS_KEY=
|
||||
export DJANGO_AWS_STORAGE_BUCKET_NAME=
|
||||
export DATABASE_URL='<see below>'
|
||||
export DATABASE_URL='<see Database setup section below>'
|
||||
export REDIS_URL='<see Redis section below>'
|
||||
|
||||
**NOTE:** *The AWS details are not required if you're using whitenoise or the built-in pythonanywhere static files service, but you do need to set them to blank, as above.*
|
||||
.. note:: The AWS details are not required if you're using whitenoise or the built-in pythonanywhere static files service, but you do need to set them to blank, as above.
|
||||
|
||||
|
||||
Database setup:
|
||||
---------------
|
||||
Database setup
|
||||
--------------
|
||||
|
||||
Go to the PythonAnywhere **Databases tab** and configure your database.
|
||||
|
||||
|
@ -109,17 +109,26 @@ Now run the migration, and collectstatic:
|
|||
source $VIRTUAL_ENV/bin/postactivate
|
||||
python manage.py migrate
|
||||
python manage.py collectstatic
|
||||
# if using django-compressor:
|
||||
python manage.py compress
|
||||
# and, optionally
|
||||
python manage.py createsuperuser
|
||||
|
||||
|
||||
Redis
|
||||
-----
|
||||
|
||||
PythonAnywhere does NOT `offer a built-in solution <https://www.pythonanywhere.com/forums/topic/1666/>`_ for Redis, however the production setup from Cookiecutter Django uses Redis as cache and requires one.
|
||||
|
||||
We recommend to signup to a separate service offering hosted Redis (e.g. `Redislab <https://redis.com/>`_) and use the URL they provide.
|
||||
|
||||
|
||||
Configure the PythonAnywhere Web Tab
|
||||
------------------------------------
|
||||
|
||||
Go to the PythonAnywhere **Web tab**, hit **Add new web app**, and choose **Manual Config**, and then the version of Python you used for your virtualenv.
|
||||
|
||||
**NOTE:** *If you're using a custom domain (not on \*.pythonanywhere.com), then you'll need to set up a CNAME with your domain registrar.*
|
||||
.. note:: If you're using a custom domain (not on \*.pythonanywhere.com), then you'll need to set up a CNAME with your domain registrar.
|
||||
|
||||
When you're redirected back to the web app config screen, set the **path to your virtualenv**. If you used virtualenvwrapper as above, you can just enter its name.
|
||||
|
||||
|
@ -152,15 +161,14 @@ Click through to the **WSGI configuration file** link (near the top) and edit th
|
|||
Back on the Web tab, hit **Reload**, and your app should be live!
|
||||
|
||||
|
||||
**NOTE:** *you may see security warnings until you set up your SSL certificates. If you
|
||||
want to supress them temporarily, set DJANGO_SECURE_SSL_REDIRECT to blank. Follow
|
||||
the instructions here to get SSL set up: https://help.pythonanywhere.com/pages/SSLOwnDomains/*
|
||||
.. note:: You may see security warnings until you set up your SSL certificates. If you want to suppress them temporarily, set ``DJANGO_SECURE_SSL_REDIRECT`` to blank. Follow `these instructions <https://help.pythonanywhere.com/pages/HTTPSSetup>`_ to get SSL set up.
|
||||
|
||||
|
||||
|
||||
Optional: static files
|
||||
----------------------
|
||||
|
||||
If you want to use the PythonAnywhere static files service instead of using whitenoise or S3, you'll find its configuration section on the Web tab. Essentially you'll need an entry to match your ``STATIC_URL`` and ``STATIC_ROOT`` settings. There's more info here: https://help.pythonanywhere.com/pages/DjangoStaticFiles
|
||||
If you want to use the PythonAnywhere static files service instead of using whitenoise or S3, you'll find its configuration section on the Web tab. Essentially you'll need an entry to match your ``STATIC_URL`` and ``STATIC_ROOT`` settings. There's more info `in this article <https://help.pythonanywhere.com/pages/DjangoStaticFiles>`_.
|
||||
|
||||
|
||||
Future deployments
|
||||
|
@ -175,7 +183,9 @@ For subsequent deployments, the procedure is much simpler. In a Bash console:
|
|||
git pull
|
||||
python manage.py migrate
|
||||
python manage.py collectstatic
|
||||
# if using django-compressor:
|
||||
python manage.py compress
|
||||
|
||||
And then go to the Web tab and hit **Reload**
|
||||
|
||||
**TIP:** *if you're really keen, you can set up git-push based deployments: https://blog.pythonanywhere.com/87/*
|
||||
.. note:: If you're really keen, you can set up git-push based deployments: https://blog.pythonanywhere.com/87/
|
||||
|
|
|
@ -7,8 +7,8 @@ Deployment with Docker
|
|||
Prerequisites
|
||||
-------------
|
||||
|
||||
* Docker 1.10+.
|
||||
* Docker Compose 1.6+
|
||||
* Docker 17.05+.
|
||||
* Docker Compose 1.17+
|
||||
|
||||
|
||||
Understanding the Docker Compose Setup
|
||||
|
@ -19,13 +19,15 @@ Before you begin, check out the ``production.yml`` file in the root of this proj
|
|||
* ``django``: your application running behind ``Gunicorn``;
|
||||
* ``postgres``: PostgreSQL database with the application's relational data;
|
||||
* ``redis``: Redis instance for caching;
|
||||
* ``caddy``: Caddy web server with HTTPS on by default.
|
||||
* ``traefik``: Traefik reverse proxy with HTTPS on by default.
|
||||
|
||||
Provided you have opted for Celery (via setting ``use_celery`` to ``y``) there are three more services:
|
||||
|
||||
* ``celeryworker`` running a Celery worker process;
|
||||
* ``celerybeat`` running a Celery beat process;
|
||||
* ``flower`` running Flower_ (for more info, check out :ref:`CeleryFlower` instructions for local environment).
|
||||
* ``flower`` running Flower_.
|
||||
|
||||
The ``flower`` service is served by Traefik over HTTPS, through the port ``5555``. For more information about Flower and its login credentials, check out :ref:`CeleryFlower` instructions for local environment.
|
||||
|
||||
.. _`Flower`: https://github.com/mher/flower
|
||||
|
||||
|
@ -35,7 +37,15 @@ Configuring the Stack
|
|||
|
||||
The majority of services above are configured through the use of environment variables. Just check out :ref:`envs` and you will know the drill.
|
||||
|
||||
To obtain logs and information about crashes in a production setup, make sure that you have access to an external Sentry instance (e.g. by creating an account with `sentry.io`_), and set the ``SENTRY_DSN`` variable.
|
||||
To obtain logs and information about crashes in a production setup, make sure that you have access to an external Sentry instance (e.g. by creating an account with `sentry.io`_), and set the ``SENTRY_DSN`` variable. Logs of level `logging.ERROR` are sent as Sentry events. Therefore, in order to send a Sentry event use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import logging
|
||||
logging.error("This event is sent to Sentry", extra={"<example_key>": "<example_value>"})
|
||||
|
||||
The `extra` parameter allows you to send additional information about the context of this error.
|
||||
|
||||
|
||||
You will probably also need to setup the Mail backend, for example by adding a `Mailgun`_ API key and a `Mailgun`_ sender domain, otherwise, the account creation view will crash and result in a 500 error when the backend attempts to send an email to the account owner.
|
||||
|
||||
|
@ -43,6 +53,11 @@ You will probably also need to setup the Mail backend, for example by adding a `
|
|||
.. _Mailgun: https://mailgun.com
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
.. include:: mailgun.rst
|
||||
|
||||
|
||||
Optional: Use AWS IAM Role for EC2 instance
|
||||
-------------------------------------------
|
||||
|
||||
|
@ -63,12 +78,38 @@ It is always better to deploy a site behind HTTPS and will become crucial as the
|
|||
|
||||
* Access to the Django admin is set up by default to require HTTPS in production or once *live*.
|
||||
|
||||
The Caddy web server used in the default configuration will get you a valid certificate from Lets 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.
|
||||
The Traefik reverse proxy used in the default configuration will get you a valid certificate from Lets 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 Traefik runs on.
|
||||
|
||||
You can read more about this here at `Automatic HTTPS`_ in the Caddy docs.
|
||||
You can read more about this feature and how to configure it, at `Automatic HTTPS`_ in the Traefik docs.
|
||||
|
||||
.. _Automatic HTTPS: https://caddyserver.com/docs/automatic-https
|
||||
.. _Automatic HTTPS: https://docs.traefik.io/https/acme/
|
||||
|
||||
.. _webpack-whitenoise-limitation:
|
||||
|
||||
Webpack without Whitenoise limitation
|
||||
-------------------------------------
|
||||
|
||||
If you opt for Webpack without Whitenoise, Webpack needs to know the static URL at build time, when running ``docker-compose build`` (See ``webpack/prod.config.js``). Depending on your setup, this URL may come from the following environment variables:
|
||||
|
||||
- ``AWS_STORAGE_BUCKET_NAME``
|
||||
- ``DJANGO_AWS_S3_CUSTOM_DOMAIN``
|
||||
- ``DJANGO_GCP_STORAGE_BUCKET_NAME``
|
||||
- ``DJANGO_AZURE_CONTAINER_NAME``
|
||||
|
||||
The Django settings are getting these values at runtime via the ``.envs/.production/.django`` file , but Docker does not read this file at build time, it only look for a ``.env`` in the root of the project. Failing to pass the values correctly will result in a page without CSS styles nor javascript.
|
||||
|
||||
To solve this, you can either:
|
||||
|
||||
1. merge all the env files into ``.env`` by running::
|
||||
|
||||
merge_production_dotenvs_in_dotenv.py
|
||||
|
||||
2. create a ``.env`` file in the root of the project with just variables you need. You'll need to also define them in ``.envs/.production/.django`` (hence duplicating them).
|
||||
3. set these variables when running the build command::
|
||||
|
||||
DJANGO_AWS_S3_CUSTOM_DOMAIN=example.com docker-compose -f production.yml build``.
|
||||
|
||||
None of these options are ideal, we're open to suggestions on how to improve this. If you think you have one, please open an issue or a pull request.
|
||||
|
||||
(Optional) Postgres Data Volume Modifications
|
||||
---------------------------------------------
|
||||
|
@ -109,10 +150,10 @@ To check the logs out, run::
|
|||
|
||||
If you want to scale your application, run::
|
||||
|
||||
docker-compose -f production.yml scale django=4
|
||||
docker-compose -f production.yml scale celeryworker=2
|
||||
docker-compose -f production.yml up --scale django=4
|
||||
docker-compose -f production.yml up --scale celeryworker=2
|
||||
|
||||
.. warning:: don't try to scale ``postgres``, ``celerybeat``, or ``caddy``.
|
||||
.. warning:: don't try to scale ``postgres``, ``celerybeat``, or ``traefik``.
|
||||
|
||||
To see how your containers are doing run::
|
||||
|
||||
|
@ -139,8 +180,14 @@ If you are using ``supervisor``, you can use this file as a starting point::
|
|||
Move it to ``/etc/supervisor/conf.d/{{cookiecutter.project_slug}}.conf`` and run::
|
||||
|
||||
supervisorctl reread
|
||||
supervisorctl update
|
||||
supervisorctl start {{cookiecutter.project_slug}}
|
||||
|
||||
For status check, run::
|
||||
|
||||
supervisorctl status
|
||||
|
||||
Media files without cloud provider
|
||||
----------------------------------
|
||||
|
||||
If you chose no cloud provider and Docker, the media files will be served by an nginx service, from a ``production_django_media`` volume. Make sure to keep this around to avoid losing any media files.
|
||||
|
|
|
@ -3,8 +3,11 @@ Getting Up and Running Locally With Docker
|
|||
|
||||
.. index:: Docker
|
||||
|
||||
The steps below will get you up and running with a local development environment.
|
||||
All of these commands assume you are in the root of your generated project.
|
||||
.. note::
|
||||
|
||||
If you're new to Docker, please be aware that some resources are cached system-wide
|
||||
and might reappear if you generate a project multiple times with the same name (e.g.
|
||||
:ref:`this issue with Postgres <docker-postgres-auth-failed>`).
|
||||
|
||||
|
||||
Prerequisites
|
||||
|
@ -12,21 +15,17 @@ Prerequisites
|
|||
|
||||
* Docker; if you don't have it yet, follow the `installation instructions`_;
|
||||
* Docker Compose; refer to the official documentation for the `installation guide`_.
|
||||
* Pre-commit; refer to the official documentation for the `pre-commit`_.
|
||||
* Cookiecutter; refer to the official GitHub repository of `Cookiecutter`_
|
||||
|
||||
.. _`installation instructions`: https://docs.docker.com/install/#supported-platforms
|
||||
.. _`installation guide`: https://docs.docker.com/compose/install/
|
||||
.. _`pre-commit`: https://pre-commit.com/#install
|
||||
.. _`Cookiecutter`: https://github.com/cookiecutter/cookiecutter
|
||||
|
||||
|
||||
Attention, Windows Users
|
||||
------------------------
|
||||
|
||||
Currently PostgreSQL (``psycopg2`` python package) is not installed inside Docker containers for Windows users, while it is required by the generated Django project. To fix this, add ``psycopg2`` to the list of requirements inside ``requirements/base.txt``::
|
||||
|
||||
# Python-PostgreSQL Database Adapter
|
||||
psycopg2==2.6.2
|
||||
|
||||
Doing this will prevent the project from being installed in an Windows-only environment (thus without usage of Docker). If you want to use this project without Docker, make sure to remove ``psycopg2`` from the requirements again.
|
||||
|
||||
Before Getting Started
|
||||
----------------------
|
||||
.. include:: generate-project-block.rst
|
||||
|
||||
Build the Stack
|
||||
---------------
|
||||
|
@ -37,6 +36,13 @@ This can take a while, especially the first time you run this particular command
|
|||
|
||||
Generally, if you want to emulate production environment use ``production.yml`` instead. And this is true for any other actions you might need to perform: whenever a switch is required, just do it!
|
||||
|
||||
Before doing any git commit, `pre-commit`_ should be installed globally on your local machine, and then::
|
||||
|
||||
$ git init
|
||||
$ pre-commit install
|
||||
|
||||
Failing to do so will result with a bunch of CI and Linter errors that can be avoided with pre-commit.
|
||||
|
||||
|
||||
Run the Stack
|
||||
-------------
|
||||
|
@ -105,7 +111,6 @@ The most important thing for us here now is ``env_file`` section enlisting ``./.
|
|||
│ ├── .django
|
||||
│ └── .postgres
|
||||
└── .production
|
||||
├── .caddy
|
||||
├── .django
|
||||
└── .postgres
|
||||
|
||||
|
@ -120,9 +125,9 @@ Consider the aforementioned ``.envs/.local/.postgres``: ::
|
|||
POSTGRES_USER=XgOWtQtJecsAbaIyslwGvFvPawftNaqO
|
||||
POSTGRES_PASSWORD=jSljDz4whHuwO3aJIgVBrqEml5Ycbghorep4uVJ4xjDYQu0LfuTZdctj7y0YcCLu
|
||||
|
||||
The three envs we are presented with here are ``POSTGRES_DB``, ``POSTGRES_USER``, and ``POSTGRES_PASSWORD`` (by the way, their values have also been generated for you). You might have figured out already where these definitions will end up; it's all the same with ``django`` and ``caddy`` service container envs.
|
||||
The three envs we are presented with here are ``POSTGRES_DB``, ``POSTGRES_USER``, and ``POSTGRES_PASSWORD`` (by the way, their values have also been generated for you). You might have figured out already where these definitions will end up; it's all the same with ``django`` service container envs.
|
||||
|
||||
One final touch: should you ever need to merge ``.envs/production/*`` in a single ``.env`` run the ``merge_production_dotenvs_in_dotenv.py``: ::
|
||||
One final touch: should you ever need to merge ``.envs/.production/*`` in a single ``.env`` run the ``merge_production_dotenvs_in_dotenv.py``: ::
|
||||
|
||||
$ python merge_production_dotenvs_in_dotenv.py
|
||||
|
||||
|
@ -139,6 +144,19 @@ This tells our computer that all future commands are specifically for the dev1 m
|
|||
|
||||
$ eval "$(docker-machine env dev1)"
|
||||
|
||||
Add 3rd party python packages
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To install a new 3rd party python package, you cannot use ``pip install <package_name>``, that would only add the package to the container. The container is ephemeral, so that new library won't be persisted if you run another container. Instead, you should modify the Docker image:
|
||||
You have to modify the relevant requirement file: base, local or production by adding: ::
|
||||
|
||||
<package_name>==<package_version>
|
||||
|
||||
To get this change picked up, you'll need to rebuild the image(s) and restart the running container: ::
|
||||
|
||||
docker-compose -f local.yml build
|
||||
docker-compose -f local.yml up
|
||||
|
||||
Debugging
|
||||
~~~~~~~~~
|
||||
|
||||
|
@ -160,17 +178,37 @@ django-debug-toolbar
|
|||
In order for ``django-debug-toolbar`` to work designate your Docker Machine IP with ``INTERNAL_IPS`` in ``local.py``.
|
||||
|
||||
|
||||
docker
|
||||
""""""
|
||||
|
||||
The ``container_name`` from the yml file can be used to check on containers with docker commands, for example: ::
|
||||
|
||||
$ docker logs <project_slug>_local_celeryworker
|
||||
$ docker top <project_slug>_local_celeryworker
|
||||
|
||||
|
||||
Notice that the ``container_name`` is generated dynamically using your project slug as a prefix
|
||||
|
||||
Mailhog
|
||||
~~~~~~~
|
||||
|
||||
When developing locally you can go with MailHog_ for email testing provided ``use_mailhog`` was set to ``y`` on setup. To proceed,
|
||||
|
||||
#. make sure ``mailhog`` container is up and running;
|
||||
#. make sure ``<project_slug>_local_mailhog`` container is up and running;
|
||||
|
||||
#. open up ``http://127.0.0.1:8025``.
|
||||
|
||||
.. _Mailhog: https://github.com/mailhog/MailHog/
|
||||
|
||||
.. _`CeleryTasks`:
|
||||
|
||||
Celery tasks in local development
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
When not using docker Celery tasks are set to run in Eager mode, so that a full stack is not needed. When using docker the task scheduler will be used by default.
|
||||
|
||||
If you need tasks to be executed on the main thread during development set ``CELERY_TASK_ALWAYS_EAGER = True`` in ``config/settings/local.py``.
|
||||
|
||||
Possible uses could be for testing, or ease of profiling with DJDT.
|
||||
|
||||
.. _`CeleryFlower`:
|
||||
|
||||
|
@ -187,3 +225,101 @@ Prerequisites:
|
|||
By default, it's enabled both in local and production environments (``local.yml`` and ``production.yml`` Docker Compose configs, respectively) through a ``flower`` service. For added security, ``flower`` requires its clients to provide authentication credentials specified as the corresponding environments' ``.envs/.local/.django`` and ``.envs/.production/.django`` ``CELERY_FLOWER_USER`` and ``CELERY_FLOWER_PASSWORD`` environment variables. Check out ``localhost:5555`` and see for yourself.
|
||||
|
||||
.. _`Flower`: https://github.com/mher/flower
|
||||
|
||||
Using Webpack or Gulp
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When using Webpack or Gulp as the ``frontend_pipeline`` option, you should access your application at the address of the ``node`` service in order to see your correct styles. This is http://localhost:3000 by default. When using any of the other ``frontend_pipeline`` options, you should use the address of the ``django`` service, http://localhost:8000.
|
||||
|
||||
Developing locally with HTTPS
|
||||
-----------------------------
|
||||
|
||||
Increasingly it is becoming necessary to develop software in a secure environment in order that there are very few changes when deploying to production. Recently Facebook changed their policies for apps/sites that use Facebook login which requires the use of an HTTPS URL for the OAuth redirect URL. So if you want to use the ``users`` application with a OAuth provider such as Facebook, securing your communication to the local development environment will be necessary.
|
||||
|
||||
In order to create a secure environment, we need to have a trusted SSL certificate installed in our Docker application.
|
||||
|
||||
#. **Let's Encrypt**
|
||||
|
||||
The official line from Let’s Encrypt is:
|
||||
|
||||
[For local development section] ... The best option: Generate your own certificate, either self-signed or signed by a local root, and trust it in your operating system’s trust store. Then use that certificate in your local web server. See below for details.
|
||||
|
||||
See `letsencrypt.org - certificates-for-localhost`_
|
||||
|
||||
.. _`letsencrypt.org - certificates-for-localhost`: https://letsencrypt.org/docs/certificates-for-localhost/
|
||||
|
||||
#. **mkcert: Valid Https Certificates For Localhost**
|
||||
|
||||
`mkcert`_ is a simple by design tool that hides all the arcane knowledge required to generate valid TLS certificates. It works for any hostname or IP, including localhost. It supports macOS, Linux, and Windows, and Firefox, Chrome and Java. It even works on mobile devices with a couple manual steps.
|
||||
|
||||
See https://blog.filippo.io/mkcert-valid-https-certificates-for-localhost/
|
||||
|
||||
.. _`mkcert`: https://github.com/FiloSottile/mkcert/blob/master/README.md#supported-root-stores
|
||||
|
||||
After installing a trusted TLS certificate, configure your docker installation. We are going to configure an ``nginx`` reverse-proxy server. This makes sure that it does not interfere with our ``traefik`` configuration that is reserved for production environments.
|
||||
|
||||
These are the places that you should configure to secure your local environment.
|
||||
|
||||
certs
|
||||
~~~~~
|
||||
|
||||
Take the certificates that you generated and place them in a folder called ``certs`` in the project's root folder. Assuming that you registered your local hostname as ``my-dev-env.local``, the certificates you will put in the folder should have the names ``my-dev-env.local.crt`` and ``my-dev-env.local.key``.
|
||||
|
||||
local.yml
|
||||
~~~~~~~~~
|
||||
|
||||
#. Add the ``nginx-proxy`` service. ::
|
||||
|
||||
...
|
||||
|
||||
nginx-proxy:
|
||||
image: jwilder/nginx-proxy:alpine
|
||||
container_name: nginx-proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./certs:/etc/nginx/certs
|
||||
restart: always
|
||||
depends_on:
|
||||
- django
|
||||
|
||||
...
|
||||
|
||||
#. Link the ``nginx-proxy`` to ``django`` through environment variables.
|
||||
|
||||
``django`` already has an ``.env`` file connected to it. Add the following variables. You should do this especially if you are working with a team and you want to keep your local environment details to yourself.
|
||||
|
||||
::
|
||||
|
||||
# HTTPS
|
||||
# ------------------------------------------------------------------------------
|
||||
VIRTUAL_HOST=my-dev-env.local
|
||||
VIRTUAL_PORT=8000
|
||||
|
||||
The services run behind the reverse proxy.
|
||||
|
||||
config/settings/local.py
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You should allow the new hostname. ::
|
||||
|
||||
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "my-dev-env.local"]
|
||||
|
||||
Rebuild your ``docker`` application. ::
|
||||
|
||||
$ docker-compose -f local.yml up -d --build
|
||||
|
||||
Go to your browser and type in your URL bar ``https://my-dev-env.local``
|
||||
|
||||
See `https with nginx`_ for more information on this configuration.
|
||||
|
||||
.. _`https with nginx`: https://codewithhugo.com/docker-compose-local-https/
|
||||
|
||||
.gitignore
|
||||
~~~~~~~~~~
|
||||
|
||||
Add ``certs/*`` to the ``.gitignore`` file. This allows the folder to be included in the repo but its contents to be ignored.
|
||||
|
||||
*This configuration is for local development environments only. Do not use this for production since you might expose your local* ``rootCA-key.pem``.
|
||||
|
|
|
@ -9,27 +9,41 @@ Setting Up Development Environment
|
|||
|
||||
Make sure to have the following on your host:
|
||||
|
||||
* Python 3.6
|
||||
* Python 3.11
|
||||
* PostgreSQL_.
|
||||
* Redis_, if using Celery
|
||||
* Cookiecutter_
|
||||
|
||||
First things first.
|
||||
|
||||
#. Create a virtualenv: ::
|
||||
|
||||
$ python3.6 -m venv <virtual env path>
|
||||
$ python3.11 -m venv <virtual env path>
|
||||
|
||||
#. Activate the virtualenv you have just created: ::
|
||||
|
||||
$ source <virtual env path>/bin/activate
|
||||
|
||||
#.
|
||||
.. include:: generate-project-block.rst
|
||||
|
||||
#. Install development requirements: ::
|
||||
|
||||
$ cd <what you have entered as the project_slug at setup stage>
|
||||
$ pip install -r requirements/local.txt
|
||||
$ git init # A git repo is required for pre-commit to install
|
||||
$ pre-commit install
|
||||
|
||||
.. note::
|
||||
|
||||
the `pre-commit` hook exists in the generated project as default.
|
||||
For the details of `pre-commit`, follow the `pre-commit`_ site.
|
||||
|
||||
#. Create a new PostgreSQL database using createdb_: ::
|
||||
|
||||
$ createdb <what you have entered as the project_slug at setup stage> -U postgres --password <password>
|
||||
$ createdb --username=postgres <project_slug>
|
||||
|
||||
``project_slug`` is what you have entered as the project_slug at the setup stage.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -62,15 +76,21 @@ First things first.
|
|||
|
||||
$ python manage.py migrate
|
||||
|
||||
#. See the application being served through Django development server: ::
|
||||
#. If you're running synchronously, see the application being served through Django development server: ::
|
||||
|
||||
$ python manage.py runserver 0.0.0.0:8000
|
||||
|
||||
or if you're running asynchronously: ::
|
||||
|
||||
$ uvicorn config.asgi:application --host 0.0.0.0 --reload --reload-include '*.html'
|
||||
|
||||
.. _PostgreSQL: https://www.postgresql.org/download/
|
||||
.. _Redis: https://redis.io/download
|
||||
.. _CookieCutter: https://github.com/cookiecutter/cookiecutter
|
||||
.. _createdb: https://www.postgresql.org/docs/current/static/app-createdb.html
|
||||
.. _initial PostgreSQL set up: http://suite.opengeo.org/docs/latest/dataadmin/pgGettingStarted/firstconnect.html
|
||||
.. _initial PostgreSQL set up: https://web.archive.org/web/20190303010033/http://suite.opengeo.org/docs/latest/dataadmin/pgGettingStarted/firstconnect.html
|
||||
.. _postgres documentation: https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html
|
||||
.. _pre-commit: https://pre-commit.com/
|
||||
.. _direnv: https://direnv.net/
|
||||
|
||||
|
||||
|
@ -118,11 +138,58 @@ In production, we have Mailgun_ configured to have your back!
|
|||
.. _Mailgun: https://www.mailgun.com/
|
||||
|
||||
|
||||
Celery
|
||||
------
|
||||
|
||||
If the project is configured to use Celery as a task scheduler then, by default, tasks are set to run on the main thread when developing locally instead of getting sent to a broker. However, if you have Redis setup on your local machine, you can set the following in ``config/settings/local.py``::
|
||||
|
||||
CELERY_TASK_ALWAYS_EAGER = False
|
||||
|
||||
Next, make sure `redis-server` is installed (per the `Getting started with Redis`_ guide) and run the server in one terminal::
|
||||
|
||||
$ redis-server
|
||||
|
||||
Start the Celery worker by running the following command in another terminal::
|
||||
|
||||
$ celery -A config.celery_app worker --loglevel=info
|
||||
|
||||
That Celery worker should be running whenever your app is running, typically as a background process,
|
||||
so that it can pick up any tasks that get queued. Learn more from the `Celery Workers Guide`_.
|
||||
|
||||
The project comes with a simple task for manual testing purposes, inside `<project_slug>/users/tasks.py`. To queue that task locally, start the Django shell, import the task, and call `delay()` on it::
|
||||
|
||||
$ python manage.py shell
|
||||
>> from <project_slug>.users.tasks import get_users_count
|
||||
>> get_users_count.delay()
|
||||
|
||||
You can also use Django admin to queue up tasks, thanks to the `django-celerybeat`_ package.
|
||||
|
||||
.. _Getting started with Redis guide: https://redis.io/docs/getting-started/
|
||||
.. _Celery Workers Guide: https://docs.celeryq.dev/en/stable/userguide/workers.html
|
||||
.. _django-celerybeat: https://django-celery-beat.readthedocs.io/en/latest/
|
||||
|
||||
|
||||
Sass Compilation & Live Reloading
|
||||
---------------------------------
|
||||
|
||||
If you’d like to take advantage of live reloading and Sass compilation you can do so with a little
|
||||
bit of preparation, see :ref:`sass-compilation-live-reload`.
|
||||
If you've opted for Gulp or Webpack as front-end pipeline, the project comes configured with `Sass`_ compilation and `live reloading`_. As you change you Sass/JS source files, the task runner will automatically rebuild the corresponding CSS and JS assets and reload them in your browser without refreshing the page.
|
||||
|
||||
#. Make sure that `Node.js`_ v18 is installed on your machine.
|
||||
#. In the project root, install the JS dependencies with::
|
||||
|
||||
$ npm install
|
||||
|
||||
#. Now - with your virtualenv activated - start the application by running::
|
||||
|
||||
$ npm run dev
|
||||
|
||||
The app will now run with live reloading enabled, applying front-end changes dynamically.
|
||||
|
||||
.. note:: The task will start 2 processes in parallel: the static assets build loop on one side, and the Django server on the other. You do NOT need to run Django as your would normally with ``manage.py runserver``.
|
||||
|
||||
.. _Node.js: http://nodejs.org/download/
|
||||
.. _Sass: https://sass-lang.com/
|
||||
.. _live reloading: https://browsersync.io
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
|
|
@ -55,8 +55,11 @@ With a single backup file copied to ``.`` that would be ::
|
|||
|
||||
$ docker cp 9c5c3f055843:/backups/backup_2018_03_13T09_05_07.sql.gz .
|
||||
|
||||
.. _`command`: https://docs.docker.com/engine/reference/commandline/cp/
|
||||
You can also get the container ID using ``docker-compose -f local.yml ps -q postgres`` so if you want to automate your backups, you don't have to check the container ID manually every time. Here is the full command ::
|
||||
|
||||
$ docker cp $(docker-compose -f local.yml ps -q postgres):/backups ./backups
|
||||
|
||||
.. _`command`: https://docs.docker.com/engine/reference/commandline/cp/
|
||||
|
||||
Restoring from the Existing Backup
|
||||
----------------------------------
|
||||
|
@ -85,3 +88,11 @@ You will see something like ::
|
|||
# ...
|
||||
ALTER TABLE
|
||||
SUCCESS: The 'my_project' database has been restored from the '/backups/backup_2018_03_13T09_05_07.sql.gz' backup.
|
||||
|
||||
|
||||
Backup to Amazon S3
|
||||
----------------------------------
|
||||
For uploading your backups to Amazon S3 you can use the aws cli container. There is an upload command for uploading the postgres /backups directory recursively and there is a download command for downloading a specific backup. The default S3 environment variables are used. ::
|
||||
|
||||
$ docker-compose -f production.yml run --rm awscli upload
|
||||
$ docker-compose -f production.yml run --rm awscli download backup_2018_03_13T09_05_07.sql.gz
|
||||
|
|
44
docs/document.rst
Normal file
44
docs/document.rst
Normal file
|
@ -0,0 +1,44 @@
|
|||
.. _document:
|
||||
|
||||
Document
|
||||
=========
|
||||
|
||||
This project uses Sphinx_ documentation generator.
|
||||
|
||||
After you have set up to `develop locally`_, run the following command from the project directory to build and serve HTML documentation: ::
|
||||
|
||||
$ make -C docs livehtml
|
||||
|
||||
If you set up your project to `develop locally with docker`_, run the following command: ::
|
||||
|
||||
$ docker-compose -f local.yml up docs
|
||||
|
||||
Navigate to port 9000 on your host to see the documentation. This will be opened automatically at `localhost`_ for local, non-docker development.
|
||||
|
||||
Note: using Docker for documentation sets up a temporary SQLite file by setting the environment variable ``DATABASE_URL=sqlite:///readthedocs.db`` in ``docs/conf.py`` to avoid a dependency on PostgreSQL.
|
||||
|
||||
Generate API documentation
|
||||
----------------------------
|
||||
|
||||
Edit the ``docs`` files and project application docstrings to create your documentation.
|
||||
|
||||
Sphinx can automatically include class and function signatures and docstrings in generated documentation.
|
||||
See the generated project documentation for more examples.
|
||||
|
||||
Setting up ReadTheDocs
|
||||
----------------------
|
||||
|
||||
To setup your documentation on `ReadTheDocs`_, you must
|
||||
|
||||
1. Go to `ReadTheDocs`_ and login/create an account
|
||||
2. Add your GitHub repository
|
||||
3. Trigger a build
|
||||
|
||||
Additionally, you can auto-build Pull Request previews, but `you must enable it`_.
|
||||
|
||||
.. _localhost: http://localhost:9000/
|
||||
.. _Sphinx: https://www.sphinx-doc.org/en/master/index.html
|
||||
.. _develop locally: ./developing-locally.html
|
||||
.. _develop locally with docker: ./developing-locally-docker.html
|
||||
.. _ReadTheDocs: https://readthedocs.org/
|
||||
.. _you must enable it: https://docs.readthedocs.io/en/latest/guides/autobuild-docs-for-pull-requests.html#autobuild-documentation-for-pull-requests
|
|
@ -6,11 +6,11 @@ FAQ
|
|||
Why is there a django.contrib.sites directory in Cookiecutter Django?
|
||||
---------------------------------------------------------------------
|
||||
|
||||
It is there to add a migration so you don't have to manually change the ``sites.Site`` record from ``example.com`` to whatever your domain is. Instead, your ``{{cookiecutter.domain_name}}`` and {{cookiecutter.project_name}} value is placed by **Cookiecutter** in the domain and name fields respectively.
|
||||
It is there to add a migration so you don't have to manually change the ``sites.Site`` record from ``example.com`` to whatever your domain is. Instead, your ``{{cookiecutter.domain_name}}`` and ``{{cookiecutter.project_name}}`` value is placed by **Cookiecutter** in the domain and name fields respectively.
|
||||
|
||||
See `0003_set_site_domain_and_name.py`_.
|
||||
|
||||
.. _`0003_set_site_domain_and_name.py`: https://github.com/pydanny/cookiecutter-django/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/%7B%7Bcookiecutter.project_slug%7D%7D/contrib/sites/migrations/0003_set_site_domain_and_name.py
|
||||
.. _`0003_set_site_domain_and_name.py`: https://github.com/cookiecutter/cookiecutter-django/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/%7B%7Bcookiecutter.project_slug%7D%7D/contrib/sites/migrations/0003_set_site_domain_and_name.py
|
||||
|
||||
|
||||
Why aren't you using just one configuration file (12-Factor App)
|
||||
|
@ -24,4 +24,4 @@ Why doesn't this follow the layout from Two Scoops of Django?
|
|||
|
||||
You may notice that some elements of this project do not exactly match what we describe in chapter 3 of `Two Scoops of Django 1.11`_. The reason for that is this project, amongst other things, serves as a test bed for trying out new ideas and concepts. Sometimes they work, sometimes they don't, but the end result is that it won't necessarily match precisely what is described in the book I co-authored.
|
||||
|
||||
.. _Two Scoops of Django 1.11: https://www.twoscoopspress.com/collections/django/products/two-scoops-of-django-1-11
|
||||
.. _Two Scoops of Django 1.11: https://www.feldroy.com/collections/django/products/two-scoops-of-django-1-11
|
||||
|
|
6
docs/generate-project-block.rst
Normal file
6
docs/generate-project-block.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
Generate a new cookiecutter-django project: ::
|
||||
|
||||
$ cookiecutter gh:cookiecutter/cookiecutter-django
|
||||
|
||||
For more information refer to
|
||||
:ref:`Project Generation Options <template-options>`.
|
|
@ -1,13 +1,14 @@
|
|||
.. cookiecutter-django documentation master file.
|
||||
|
||||
Welcome to Cookiecutter Django's documentation!
|
||||
====================================================================
|
||||
===============================================
|
||||
|
||||
A Cookiecutter_ template for Django.
|
||||
Powered by Cookiecutter_, Cookiecutter Django is a project template for jumpstarting production-ready Django projects. The template offers a number of generation options, we invite you to check the :ref:`dedicated page <template-options>` to learn more about each of them.
|
||||
|
||||
.. _cookiecutter: https://github.com/audreyr/cookiecutter
|
||||
.. _cookiecutter: https://github.com/cookiecutter/cookiecutter
|
||||
|
||||
Contents:
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@ -18,15 +19,18 @@ Contents:
|
|||
settings
|
||||
linters
|
||||
testing
|
||||
document
|
||||
deployment-on-pythonanywhere
|
||||
deployment-on-heroku
|
||||
deployment-with-docker
|
||||
docker-postgres-backups
|
||||
websocket
|
||||
faq
|
||||
troubleshooting
|
||||
contributing
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
------------------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
|
|
@ -19,13 +19,13 @@ The config for flake8 is located in setup.cfg. It specifies:
|
|||
pylint
|
||||
------
|
||||
|
||||
This is included in flake8's checks, but you can also run it separately to see a more detailed report: ::
|
||||
To run pylint: ::
|
||||
|
||||
$ pylint <python files that you wish to lint>
|
||||
|
||||
The config for pylint is located in .pylintrc. It specifies:
|
||||
|
||||
* Use the pylint_common and pylint_django plugins. If using Celery, also use pylint_celery.
|
||||
* Use the pylint_django plugin. If using Celery, also use pylint_celery.
|
||||
* Set max line length to 120 chars
|
||||
* Disable linting messages for missing docstring and invalid name
|
||||
* max-parents=13
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
.. _sass-compilation-live-reload:
|
||||
|
||||
Sass Compilation & Live Reloading
|
||||
=================================
|
||||
|
||||
If you'd like to take advantage of `live reload`_ and Sass compilation:
|
||||
|
||||
- Make sure that nodejs_ is installed. Then in the project root run::
|
||||
|
||||
$ npm install
|
||||
|
||||
.. _nodejs: http://nodejs.org/download/
|
||||
|
||||
- Now you just need::
|
||||
|
||||
$ npm run dev
|
||||
|
||||
The base app will now run as it would with the usual ``manage.py runserver`` but with live reloading and Sass compilation enabled.
|
||||
When changing your Sass files, they will be automatically recompiled and change will be reflected in your browser without refreshing.
|
||||
|
||||
To get live reloading to work you'll probably need to install an `appropriate browser extension`_
|
||||
|
||||
.. _live reload: http://livereload.com/
|
||||
.. _appropriate browser extension: http://livereload.com/extensions/
|
13
docs/mailgun.rst
Normal file
13
docs/mailgun.rst
Normal file
|
@ -0,0 +1,13 @@
|
|||
If your email server used to send email isn't configured properly (Mailgun by default),
|
||||
attempting to send an email will cause an Internal Server Error.
|
||||
|
||||
By default, ``django-allauth`` is setup to `have emails verifications mandatory`_,
|
||||
which means it'll send a verification email when an unverified user tries to
|
||||
log-in or when someone tries to sign-up.
|
||||
|
||||
This may happen just after you've setup your Mailgun account, which is running in a
|
||||
sandbox subdomain by default. Either add your email to the list of authorized recipients
|
||||
or verify your domain.
|
||||
|
||||
|
||||
.. _have emails verifications mandatory: https://django-allauth.readthedocs.io/en/latest/configuration.html?highlight=ACCOUNT_EMAIL_VERIFICATION
|
|
@ -1,6 +1,12 @@
|
|||
.. _template-options:
|
||||
|
||||
Project Generation Options
|
||||
==========================
|
||||
|
||||
This page describes all the template options that will be prompted by the `cookiecutter CLI`_ prior to generating your project.
|
||||
|
||||
.. _cookiecutter CLI: https://github.com/cookiecutter/cookiecutter
|
||||
|
||||
project_name:
|
||||
Your project's human-readable name, capitals and spaces allowed.
|
||||
|
||||
|
@ -18,6 +24,13 @@ author_name:
|
|||
email:
|
||||
The email address you want to identify yourself in the project.
|
||||
|
||||
username_type:
|
||||
The type of username you want to use in the project. This can be either
|
||||
``username`` or ``email``. If you choose ``username``, the ``email`` field
|
||||
will be included. If you choose ``email``, the ``username`` field will be
|
||||
excluded. It is best practice to always include an email field, so there is
|
||||
no option for having just the ``username`` field.
|
||||
|
||||
domain_name:
|
||||
The domain name you plan to use for your project once it goes live.
|
||||
Note that it can be safely changed later on whenever you need to.
|
||||
|
@ -49,27 +62,50 @@ use_docker:
|
|||
postgresql_version:
|
||||
Select a PostgreSQL_ version to use. The choices are:
|
||||
|
||||
1. 10.3
|
||||
2. 10.2
|
||||
3. 10.1
|
||||
4. 9.6
|
||||
5. 9.5
|
||||
6. 9.4
|
||||
7. 9.3
|
||||
1. 14
|
||||
2. 13
|
||||
3. 12
|
||||
4. 11
|
||||
5. 10
|
||||
|
||||
js_task_runner:
|
||||
Select a JavaScript task runner. The choices are:
|
||||
cloud_provider:
|
||||
Select a cloud provider for static & media files. The choices are:
|
||||
|
||||
1. AWS_
|
||||
2. GCP_
|
||||
3. Azure_
|
||||
4. None
|
||||
|
||||
If you choose no cloud provider and docker, the production stack will serve the media files via an nginx Docker service. Without Docker, the media files won't work.
|
||||
|
||||
mail_service:
|
||||
Select an email service that Django-Anymail provides
|
||||
|
||||
1. Mailgun_
|
||||
2. `Amazon SES`_
|
||||
3. Mailjet_
|
||||
4. Mandrill_
|
||||
5. Postmark_
|
||||
6. SendGrid_
|
||||
7. SendinBlue_
|
||||
8. SparkPost_
|
||||
9. `Other SMTP`_
|
||||
|
||||
use_async:
|
||||
Indicates whether the project should use web sockets with Uvicorn + Gunicorn.
|
||||
|
||||
use_drf:
|
||||
Indicates whether the project should be configured to use `Django Rest Framework`_.
|
||||
|
||||
frontend_pipeline:
|
||||
Select a pipeline to compile and optimise frontend assets (JS, CSS, ...):
|
||||
|
||||
1. None
|
||||
2. Gulp_
|
||||
2. `Django Compressor`_
|
||||
3. `Gulp`_
|
||||
4. `Webpack`_
|
||||
|
||||
custom_bootstrap_compilation:
|
||||
Indicates whether the project should support Bootstrap recompilation
|
||||
via the selected JavaScript task runner's task. This can be useful
|
||||
for real-time Bootstrap variable alteration.
|
||||
|
||||
use_compressor:
|
||||
Indicates whether the project should be configured to use `Django Compressor`_.
|
||||
Both Gulp and Webpack support Bootstrap recompilation with real-time variables alteration.
|
||||
|
||||
use_celery:
|
||||
Indicates whether the project should be configured to use Celery_.
|
||||
|
@ -87,8 +123,13 @@ use_heroku:
|
|||
Indicates whether the project should be configured so as to be deployable
|
||||
to Heroku_.
|
||||
|
||||
use_travisci:
|
||||
Indicates whether the project should be configured to use `Travis CI`_.
|
||||
ci_tool:
|
||||
Select a CI tool for running tests. The choices are:
|
||||
|
||||
1. None
|
||||
2. `Travis CI`_
|
||||
3. `Gitlab CI`_
|
||||
4. `Github Actions`_
|
||||
|
||||
keep_local_envs_in_vcs:
|
||||
Indicates whether the project's ``.envs/.local/`` should be kept in VCS
|
||||
|
@ -114,6 +155,23 @@ debug:
|
|||
.. _PostgreSQL: https://www.postgresql.org/docs/
|
||||
|
||||
.. _Gulp: https://github.com/gulpjs/gulp
|
||||
.. _Webpack: https://webpack.js.org
|
||||
|
||||
.. _AWS: https://aws.amazon.com/s3/
|
||||
.. _GCP: https://cloud.google.com/storage/
|
||||
.. _Azure: https://azure.microsoft.com/en-us/products/storage/blobs/
|
||||
|
||||
.. _Amazon SES: https://aws.amazon.com/ses/
|
||||
.. _Mailgun: https://www.mailgun.com
|
||||
.. _Mailjet: https://www.mailjet.com
|
||||
.. _Mandrill: http://mandrill.com
|
||||
.. _Postmark: https://postmarkapp.com
|
||||
.. _SendGrid: https://sendgrid.com
|
||||
.. _SendinBlue: https://www.sendinblue.com
|
||||
.. _SparkPost: https://www.sparkpost.com
|
||||
.. _Other SMTP: https://anymail.readthedocs.io/en/stable/
|
||||
|
||||
.. _Django Rest Framework: https://github.com/encode/django-rest-framework/
|
||||
|
||||
.. _Django Compressor: https://github.com/django-compressor/django-compressor
|
||||
|
||||
|
@ -128,3 +186,7 @@ debug:
|
|||
.. _Heroku: https://github.com/heroku/heroku-buildpack-python
|
||||
|
||||
.. _Travis CI: https://travis-ci.org/
|
||||
|
||||
.. _GitLab CI: https://docs.gitlab.com/ee/ci/
|
||||
|
||||
.. _Github Actions: https://docs.github.com/en/actions
|
||||
|
|
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
sphinx==6.2.1
|
||||
sphinx-rtd-theme==1.2.2
|
||||
myst-parser==2.0.0
|
|
@ -22,7 +22,6 @@ DATABASE_URL DATABASES auto w/ Dock
|
|||
DJANGO_ADMIN_URL n/a 'admin/' raises error
|
||||
DJANGO_DEBUG DEBUG True False
|
||||
DJANGO_SECRET_KEY SECRET_KEY auto-generated raises error
|
||||
DJANGO_SECURE_BROWSER_XSS_FILTER SECURE_BROWSER_XSS_FILTER n/a True
|
||||
DJANGO_SECURE_SSL_REDIRECT SECURE_SSL_REDIRECT n/a True
|
||||
DJANGO_SECURE_CONTENT_TYPE_NOSNIFF SECURE_CONTENT_TYPE_NOSNIFF n/a True
|
||||
DJANGO_SECURE_FRAME_DENY SECURE_FRAME_DENY n/a True
|
||||
|
@ -44,11 +43,36 @@ CELERY_BROKER_URL CELERY_BROKER_URL auto w/ Dock
|
|||
DJANGO_AWS_ACCESS_KEY_ID AWS_ACCESS_KEY_ID n/a raises error
|
||||
DJANGO_AWS_SECRET_ACCESS_KEY AWS_SECRET_ACCESS_KEY n/a raises error
|
||||
DJANGO_AWS_STORAGE_BUCKET_NAME AWS_STORAGE_BUCKET_NAME n/a raises error
|
||||
DJANGO_AWS_S3_REGION_NAME AWS_S3_REGION_NAME n/a None
|
||||
DJANGO_AWS_S3_CUSTOM_DOMAIN AWS_S3_CUSTOM_DOMAIN n/a None
|
||||
DJANGO_AWS_S3_MAX_MEMORY_SIZE AWS_S3_MAX_MEMORY_SIZE n/a 100_000_000
|
||||
DJANGO_GCP_STORAGE_BUCKET_NAME GS_BUCKET_NAME n/a raises error
|
||||
GOOGLE_APPLICATION_CREDENTIALS n/a n/a raises error
|
||||
DJANGO_AZURE_ACCOUNT_KEY AZURE_ACCOUNT_KEY n/a raises error
|
||||
DJANGO_AZURE_ACCOUNT_NAME AZURE_ACCOUNT_NAME n/a raises error
|
||||
DJANGO_AZURE_CONTAINER_NAME AZURE_CONTAINER n/a raises error
|
||||
SENTRY_DSN SENTRY_DSN n/a raises error
|
||||
DJANGO_SENTRY_CLIENT SENTRY_CLIENT n/a raven.contrib.django.raven_compat.DjangoClient
|
||||
SENTRY_ENVIRONMENT n/a n/a production
|
||||
SENTRY_TRACES_SAMPLE_RATE n/a n/a 0.0
|
||||
DJANGO_SENTRY_LOG_LEVEL SENTRY_LOG_LEVEL n/a logging.INFO
|
||||
MAILGUN_API_KEY MAILGUN_ACCESS_KEY n/a raises error
|
||||
MAILGUN_API_KEY MAILGUN_API_KEY n/a raises error
|
||||
MAILGUN_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error
|
||||
MAILGUN_API_URL n/a n/a "https://api.mailgun.net/v3"
|
||||
MAILJET_API_KEY MAILJET_API_KEY n/a raises error
|
||||
MAILJET_SECRET_KEY MAILJET_SECRET_KEY n/a raises error
|
||||
MAILJET_API_URL n/a n/a "https://api.mailjet.com/v3"
|
||||
MANDRILL_API_KEY MANDRILL_API_KEY n/a raises error
|
||||
MANDRILL_API_URL n/a n/a "https://mandrillapp.com/api/1.0"
|
||||
POSTMARK_SERVER_TOKEN POSTMARK_SERVER_TOKEN n/a raises error
|
||||
POSTMARK_API_URL n/a n/a "https://api.postmarkapp.com/"
|
||||
SENDGRID_API_KEY SENDGRID_API_KEY n/a raises error
|
||||
SENDGRID_GENERATE_MESSAGE_ID True n/a raises error
|
||||
SENDGRID_MERGE_FIELD_FORMAT None n/a raises error
|
||||
SENDGRID_API_URL n/a n/a "https://api.sendgrid.com/v3/"
|
||||
SENDINBLUE_API_KEY SENDINBLUE_API_KEY n/a raises error
|
||||
SENDINBLUE_API_URL n/a n/a "https://api.sendinblue.com/v3/"
|
||||
SPARKPOST_API_KEY SPARKPOST_API_KEY n/a raises error
|
||||
SPARKPOST_API_URL n/a n/a "https://api.sparkpost.com/api/v1"
|
||||
======================================= =========================== ============================================== ======================================================================
|
||||
|
||||
--------------------------
|
||||
|
|
|
@ -19,20 +19,25 @@ You will get a readout of the `users` app that has already been set up with test
|
|||
|
||||
If you set up your project to `develop locally with docker`_, run the following command: ::
|
||||
|
||||
$ docker-compose -f local.yml run django pytest
|
||||
$ docker-compose -f local.yml run --rm django pytest
|
||||
|
||||
Targetting particular apps for testing in ``docker`` follows a similar pattern as previously shown above.
|
||||
Targeting particular apps for testing in ``docker`` follows a similar pattern as previously shown above.
|
||||
|
||||
Coverage
|
||||
--------
|
||||
|
||||
You should build your tests to provide the highest level of **code coverage**. You can run the ``pytest`` with code ``coverage`` by typing in the following command: ::
|
||||
|
||||
$ docker-compose -f local.yml run django coverage run -m pytest
|
||||
$ coverage run -m pytest
|
||||
|
||||
Once the tests are complete, in order to see the code coverage, run the following command: ::
|
||||
|
||||
$ docker-compose -f local.yml run django coverage report
|
||||
$ coverage report
|
||||
|
||||
If you're running the project locally with Docker, use these commands instead: ::
|
||||
|
||||
$ docker-compose -f local.yml run --rm django coverage run -m pytest
|
||||
$ docker-compose -f local.yml run --rm django coverage report
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -49,8 +54,8 @@ Once the tests are complete, in order to see the code coverage, run the followin
|
|||
Since this is a fresh install, and there are no tests built using the Python `unittest`_ library yet, you should get feedback that says there were no tests carried out.
|
||||
|
||||
.. _Pytest: https://docs.pytest.org/en/latest/example/simple.html
|
||||
.. _develop locally: ../developing-locally.rst
|
||||
.. _develop locally with docker: ..../developing-locally-docker.rst
|
||||
.. _develop locally: ./developing-locally.html
|
||||
.. _develop locally with docker: ./developing-locally-docker.html
|
||||
.. _customize: https://docs.pytest.org/en/latest/customize.html
|
||||
.. _unittest: https://docs.python.org/3/library/unittest.html#module-unittest
|
||||
.. _configuring: https://coverage.readthedocs.io/en/v4.5.x/config.html
|
||||
.. _configuring: https://coverage.readthedocs.io/en/latest/config.html
|
||||
|
|
|
@ -1,15 +1,61 @@
|
|||
Troubleshooting
|
||||
=====================================
|
||||
===============
|
||||
|
||||
This page contains some advice about errors and problems commonly encountered during the development of Cookiecutter Django applications.
|
||||
|
||||
Server Error on sign-up/log-in
|
||||
------------------------------
|
||||
|
||||
Make sure you have configured the mail backend (e.g. Mailgun) by adding the API key and sender domain
|
||||
|
||||
.. include:: mailgun.rst
|
||||
|
||||
.. _docker-postgres-auth-failed:
|
||||
|
||||
Docker: Postgres authentication failed
|
||||
--------------------------------------
|
||||
|
||||
Examples of logs::
|
||||
|
||||
postgres_1 | 2018-06-07 19:11:23.963 UTC [81] FATAL: password authentication failed for user "pydanny"
|
||||
postgres_1 | 2018-06-07 19:11:23.963 UTC [81] DETAIL: Password does not match for user "pydanny".
|
||||
postgres_1 | Connection matched pg_hba.conf line 95: "host all all all md5"
|
||||
|
||||
If you recreate the project multiple times with the same name, Docker would preserve the volumes for the postgres container between projects. Here is what happens:
|
||||
|
||||
#. You generate the project the first time. The .env postgres file is populated with the random password
|
||||
#. You run the docker-compose and the containers are created. The postgres container creates the database based on the .env file credentials
|
||||
#. You "regenerate" the project with the same name, so the postgres .env file is populated with a new random password
|
||||
#. You run docker-compose. Since the names of the containers are the same, docker will try to start them (not create them from scratch i.e. it won't execute the Dockerfile to recreate the database). When this happens, it tries to start the database based on the new credentials which do not match the ones that the database was created with, and you get the error message above.
|
||||
|
||||
To fix this, you can either:
|
||||
|
||||
- Clear your project-related Docker cache with ``docker-compose -f local.yml down --volumes --rmi all``.
|
||||
- Use the Docker volume sub-commands to find volumes (`ls`_) and remove them (`rm`_).
|
||||
- Use the `prune`_ command to clear system-wide (use with care!).
|
||||
|
||||
.. _ls: https://docs.docker.com/engine/reference/commandline/volume_ls/
|
||||
.. _rm: https://docs.docker.com/engine/reference/commandline/volume_rm/
|
||||
.. _prune: https://docs.docker.com/v17.09/engine/reference/commandline/system_prune/
|
||||
|
||||
Variable is not set. Defaulting to a blank string
|
||||
-------------------------------------------------
|
||||
|
||||
Example::
|
||||
|
||||
WARN[0000] The "DJANGO_AWS_STORAGE_BUCKET_NAME" variable is not set. Defaulting to a blank string.
|
||||
WARN[0000] The "DJANGO_AWS_S3_CUSTOM_DOMAIN" variable is not set. Defaulting to a blank string.
|
||||
|
||||
You have probably opted for Docker + Webpack without Whitenoise. This is a know limitation of the combination, which needs a little bit of manual intervention. See the :ref:`dedicated section about it <webpack-whitenoise-limitation>`.
|
||||
|
||||
Others
|
||||
------
|
||||
|
||||
#. ``project_slug`` must be a valid Python module name or you will have issues on imports.
|
||||
|
||||
#. ``jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'now'.``: please upgrade your cookiecutter version to >= 1.4 (see `#528`_)
|
||||
|
||||
#. Internal server error on user registration: make sure you have configured the mail backend (e.g. Mailgun) by adding the API key and sender domain
|
||||
|
||||
#. New apps not getting created in project root: This is the expected behavior, because cookiecutter-django does not change the way that django startapp works, you'll have to fix this manually (see `#1725`_)
|
||||
|
||||
.. _#528: https://github.com/pydanny/cookiecutter-django/issues/528#issuecomment-212650373
|
||||
.. _#1725: https://github.com/pydanny/cookiecutter-django/issues/1725#issuecomment-407493176
|
||||
.. _#528: https://github.com/cookiecutter/cookiecutter-django/issues/528#issuecomment-212650373
|
||||
.. _#1725: https://github.com/cookiecutter/cookiecutter-django/issues/1725#issuecomment-407493176
|
||||
|
|
25
docs/websocket.rst
Normal file
25
docs/websocket.rst
Normal file
|
@ -0,0 +1,25 @@
|
|||
.. _websocket:
|
||||
|
||||
=========
|
||||
Websocket
|
||||
=========
|
||||
|
||||
You can enable web sockets if you select ``use_async`` option when creating a project. That indicates whether the project can use web sockets with Uvicorn + Gunicorn.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
JavaScript example: ::
|
||||
|
||||
> ws = new WebSocket('ws://localhost:8000/') // or 'wss://<mydomain.com>/' in prod
|
||||
WebSocket {url: "ws://localhost:8000/", readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, …}
|
||||
> ws.onmessage = event => console.log(event.data)
|
||||
event => console.log(event.data)
|
||||
> ws.send("ping")
|
||||
undefined
|
||||
pong!
|
||||
|
||||
|
||||
If you don't use Traefik, you might have to configure your reverse proxy accordingly (example with Nginx_).
|
||||
|
||||
.. _Nginx: https://www.nginx.com/blog/websocket-nginx/
|
|
@ -5,10 +5,12 @@ NOTE:
|
|||
can potentially be run in Python 2.x environment
|
||||
(at least so we presume in `pre_gen_project.py`).
|
||||
|
||||
TODO: ? restrict Cookiecutter Django project initialization to Python 3.x environments only
|
||||
TODO: restrict Cookiecutter Django project initialization to
|
||||
Python 3.x environments only
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
|
@ -32,10 +34,7 @@ DEBUG_VALUE = "debug"
|
|||
|
||||
|
||||
def remove_open_source_files():
|
||||
file_names = [
|
||||
"CONTRIBUTORS.txt",
|
||||
"LICENSE",
|
||||
]
|
||||
file_names = ["CONTRIBUTORS.txt", "LICENSE"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
@ -46,6 +45,24 @@ def remove_gplv3_files():
|
|||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_custom_user_manager_files():
|
||||
os.remove(
|
||||
os.path.join(
|
||||
"{{cookiecutter.project_slug}}",
|
||||
"users",
|
||||
"managers.py",
|
||||
)
|
||||
)
|
||||
os.remove(
|
||||
os.path.join(
|
||||
"{{cookiecutter.project_slug}}",
|
||||
"users",
|
||||
"tests",
|
||||
"test_managers.py",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def remove_pycharm_files():
|
||||
idea_dir_path = ".idea"
|
||||
if os.path.exists(idea_dir_path):
|
||||
|
@ -62,6 +79,10 @@ def remove_docker_files():
|
|||
file_names = ["local.yml", "production.yml", ".dockerignore"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
if "{{ cookiecutter.use_pycharm }}".lower() == "y":
|
||||
file_names = ["docker_compose_up_django.xml", "docker_compose_up_docs.xml"]
|
||||
for file_name in file_names:
|
||||
os.remove(os.path.join(".idea", "runConfigurations", file_name))
|
||||
|
||||
|
||||
def remove_utility_files():
|
||||
|
@ -71,10 +92,15 @@ def remove_utility_files():
|
|||
def remove_heroku_files():
|
||||
file_names = ["Procfile", "runtime.txt", "requirements.txt"]
|
||||
for file_name in file_names:
|
||||
if file_name == "requirements.txt" and "{{ cookiecutter.use_travisci }}".lower() == "y":
|
||||
if file_name == "requirements.txt" and "{{ cookiecutter.ci_tool }}".lower() == "travis":
|
||||
# don't remove the file if we are using travisci but not using heroku
|
||||
continue
|
||||
os.remove(file_name)
|
||||
shutil.rmtree("bin")
|
||||
|
||||
|
||||
def remove_sass_files():
|
||||
shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "static", "sass"))
|
||||
|
||||
|
||||
def remove_gulp_files():
|
||||
|
@ -83,30 +109,135 @@ def remove_gulp_files():
|
|||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_webpack_files():
|
||||
shutil.rmtree("webpack")
|
||||
remove_vendors_js()
|
||||
|
||||
|
||||
def remove_vendors_js():
|
||||
vendors_js_path = os.path.join(
|
||||
"{{ cookiecutter.project_slug }}",
|
||||
"static",
|
||||
"js",
|
||||
"vendors.js",
|
||||
)
|
||||
if os.path.exists(vendors_js_path):
|
||||
os.remove(vendors_js_path)
|
||||
|
||||
|
||||
def remove_packagejson_file():
|
||||
file_names = ["package.json"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_celery_app():
|
||||
shutil.rmtree(os.path.join("{{ cookiecutter.project_slug }}", "taskapp"))
|
||||
def update_package_json(remove_dev_deps=None, remove_keys=None, scripts=None):
|
||||
remove_dev_deps = remove_dev_deps or []
|
||||
remove_keys = remove_keys or []
|
||||
scripts = scripts or {}
|
||||
with open("package.json", mode="r") as fd:
|
||||
content = json.load(fd)
|
||||
for package_name in remove_dev_deps:
|
||||
content["devDependencies"].pop(package_name)
|
||||
for key in remove_keys:
|
||||
content.pop(key)
|
||||
content["scripts"].update(scripts)
|
||||
with open("package.json", mode="w") as fd:
|
||||
json.dump(content, fd, ensure_ascii=False, indent=2)
|
||||
fd.write("\n")
|
||||
|
||||
|
||||
def handle_js_runner(choice, use_docker, use_async):
|
||||
if choice == "Gulp":
|
||||
update_package_json(
|
||||
remove_dev_deps=[
|
||||
"@babel/core",
|
||||
"@babel/preset-env",
|
||||
"babel-loader",
|
||||
"concurrently",
|
||||
"css-loader",
|
||||
"mini-css-extract-plugin",
|
||||
"postcss-loader",
|
||||
"postcss-preset-env",
|
||||
"sass-loader",
|
||||
"webpack",
|
||||
"webpack-bundle-tracker",
|
||||
"webpack-cli",
|
||||
"webpack-dev-server",
|
||||
"webpack-merge",
|
||||
],
|
||||
remove_keys=["babel"],
|
||||
scripts={
|
||||
"dev": "gulp",
|
||||
"build": "gulp generate-assets",
|
||||
},
|
||||
)
|
||||
remove_webpack_files()
|
||||
elif choice == "Webpack":
|
||||
scripts = {
|
||||
"dev": "webpack serve --config webpack/dev.config.js",
|
||||
"build": "webpack --config webpack/prod.config.js",
|
||||
}
|
||||
remove_dev_deps = [
|
||||
"browser-sync",
|
||||
"cssnano",
|
||||
"gulp",
|
||||
"gulp-imagemin",
|
||||
"gulp-plumber",
|
||||
"gulp-postcss",
|
||||
"gulp-rename",
|
||||
"gulp-sass",
|
||||
"gulp-uglify-es",
|
||||
]
|
||||
if not use_docker:
|
||||
dev_django_cmd = (
|
||||
"uvicorn config.asgi:application --reload" if use_async else "python manage.py runserver_plus"
|
||||
)
|
||||
scripts.update(
|
||||
{
|
||||
"dev": "concurrently npm:dev:*",
|
||||
"dev:webpack": "webpack serve --config webpack/dev.config.js",
|
||||
"dev:django": dev_django_cmd,
|
||||
}
|
||||
)
|
||||
else:
|
||||
remove_dev_deps.append("concurrently")
|
||||
update_package_json(remove_dev_deps=remove_dev_deps, scripts=scripts)
|
||||
remove_gulp_files()
|
||||
|
||||
|
||||
def remove_celery_files():
|
||||
file_names = [
|
||||
os.path.join("config", "celery_app.py"),
|
||||
os.path.join("{{ cookiecutter.project_slug }}", "users", "tasks.py"),
|
||||
os.path.join("{{ cookiecutter.project_slug }}", "users", "tests", "test_tasks.py"),
|
||||
]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_async_files():
|
||||
file_names = [
|
||||
os.path.join("config", "asgi.py"),
|
||||
os.path.join("config", "websocket.py"),
|
||||
]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_dottravisyml_file():
|
||||
os.remove(".travis.yml")
|
||||
|
||||
|
||||
def append_to_project_gitignore(path):
|
||||
gitignore_file_path = ".gitignore"
|
||||
with open(gitignore_file_path, "a") as gitignore_file:
|
||||
gitignore_file.write(path)
|
||||
gitignore_file.write(os.linesep)
|
||||
def remove_dotgitlabciyml_file():
|
||||
os.remove(".gitlab-ci.yml")
|
||||
|
||||
|
||||
def generate_random_string(
|
||||
length, using_digits=False, using_ascii_letters=False, using_punctuation=False
|
||||
):
|
||||
def remove_dotgithub_folder():
|
||||
shutil.rmtree(".github")
|
||||
|
||||
|
||||
def generate_random_string(length, using_digits=False, using_ascii_letters=False, using_punctuation=False):
|
||||
"""
|
||||
Example:
|
||||
opting out for 50 symbol-long, [a-z][A-Z][0-9] string
|
||||
|
@ -134,8 +265,8 @@ def set_flag(file_path, flag, value=None, formatted=None, *args, **kwargs):
|
|||
random_string = generate_random_string(*args, **kwargs)
|
||||
if random_string is None:
|
||||
print(
|
||||
"We couldn't find a secure pseudo-random number generator on your system. "
|
||||
"Please, make sure to manually {} later.".format(flag)
|
||||
"We couldn't find a secure pseudo-random number generator on your "
|
||||
"system. Please, make sure to manually {} later.".format(flag)
|
||||
)
|
||||
random_string = flag
|
||||
if formatted is not None:
|
||||
|
@ -183,11 +314,7 @@ def generate_postgres_user(debug=False):
|
|||
|
||||
|
||||
def set_postgres_user(file_path, value):
|
||||
postgres_user = set_flag(
|
||||
file_path,
|
||||
"!!!SET POSTGRES_USER!!!",
|
||||
value=value,
|
||||
)
|
||||
postgres_user = set_flag(file_path, "!!!SET POSTGRES_USER!!!", value=value)
|
||||
return postgres_user
|
||||
|
||||
|
||||
|
@ -204,11 +331,7 @@ def set_postgres_password(file_path, value=None):
|
|||
|
||||
|
||||
def set_celery_flower_user(file_path, value):
|
||||
celery_flower_user = set_flag(
|
||||
file_path,
|
||||
"!!!SET CELERY_FLOWER_USER!!!",
|
||||
value=value,
|
||||
)
|
||||
celery_flower_user = set_flag(file_path, "!!!SET CELERY_FLOWER_USER!!!", value=value)
|
||||
return celery_flower_user
|
||||
|
||||
|
||||
|
@ -224,17 +347,13 @@ def set_celery_flower_password(file_path, value=None):
|
|||
return celery_flower_password
|
||||
|
||||
|
||||
def append_to_gitignore_file(s):
|
||||
def append_to_gitignore_file(ignored_line):
|
||||
with open(".gitignore", "a") as gitignore_file:
|
||||
gitignore_file.write(s)
|
||||
gitignore_file.write(os.linesep)
|
||||
gitignore_file.write(ignored_line)
|
||||
gitignore_file.write("\n")
|
||||
|
||||
|
||||
def set_flags_in_envs(
|
||||
postgres_user,
|
||||
celery_flower_user,
|
||||
debug=False,
|
||||
):
|
||||
def set_flags_in_envs(postgres_user, celery_flower_user, debug=False):
|
||||
local_django_envs_path = os.path.join(".envs", ".local", ".django")
|
||||
production_django_envs_path = os.path.join(".envs", ".production", ".django")
|
||||
local_postgres_envs_path = os.path.join(".envs", ".local", ".postgres")
|
||||
|
@ -262,6 +381,7 @@ def set_flags_in_settings_files():
|
|||
def remove_envs_and_associated_files():
|
||||
shutil.rmtree(".envs")
|
||||
os.remove("merge_production_dotenvs_in_dotenv.py")
|
||||
shutil.rmtree("tests")
|
||||
|
||||
|
||||
def remove_celery_compose_dirs():
|
||||
|
@ -269,6 +389,26 @@ def remove_celery_compose_dirs():
|
|||
shutil.rmtree(os.path.join("compose", "production", "django", "celery"))
|
||||
|
||||
|
||||
def remove_node_dockerfile():
|
||||
shutil.rmtree(os.path.join("compose", "local", "node"))
|
||||
|
||||
|
||||
def remove_aws_dockerfile():
|
||||
shutil.rmtree(os.path.join("compose", "production", "aws"))
|
||||
|
||||
|
||||
def remove_drf_starter_files():
|
||||
os.remove(os.path.join("config", "api_router.py"))
|
||||
shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "users", "api"))
|
||||
os.remove(os.path.join("{{cookiecutter.project_slug}}", "users", "tests", "test_drf_urls.py"))
|
||||
os.remove(os.path.join("{{cookiecutter.project_slug}}", "users", "tests", "test_drf_views.py"))
|
||||
os.remove(os.path.join("{{cookiecutter.project_slug}}", "users", "tests", "test_swagger.py"))
|
||||
|
||||
|
||||
def remove_storages_module():
|
||||
os.remove(os.path.join("{{cookiecutter.project_slug}}", "utils", "storages.py"))
|
||||
|
||||
|
||||
def main():
|
||||
debug = "{{ cookiecutter.debug }}".lower() == "y"
|
||||
|
||||
|
@ -284,6 +424,9 @@ def main():
|
|||
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
|
||||
remove_gplv3_files()
|
||||
|
||||
if "{{ cookiecutter.username_type }}" == "username":
|
||||
remove_custom_user_manager_files()
|
||||
|
||||
if "{{ cookiecutter.use_pycharm }}".lower() == "n":
|
||||
remove_pycharm_files()
|
||||
|
||||
|
@ -292,13 +435,13 @@ def main():
|
|||
else:
|
||||
remove_docker_files()
|
||||
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "y" and "{{ cookiecutter.cloud_provider}}" != "AWS":
|
||||
remove_aws_dockerfile()
|
||||
|
||||
if "{{ cookiecutter.use_heroku }}".lower() == "n":
|
||||
remove_heroku_files()
|
||||
|
||||
if (
|
||||
"{{ cookiecutter.use_docker }}".lower() == "n"
|
||||
and "{{ cookiecutter.use_heroku }}".lower() == "n"
|
||||
):
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "n" and "{{ cookiecutter.use_heroku }}".lower() == "n":
|
||||
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
|
||||
print(
|
||||
INFO + ".env(s) are only utilized when Docker Compose and/or "
|
||||
|
@ -312,33 +455,47 @@ def main():
|
|||
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
|
||||
append_to_gitignore_file("!.envs/.local/")
|
||||
|
||||
if "{{ cookiecutter.js_task_runner}}".lower() == "none":
|
||||
if "{{ cookiecutter.frontend_pipeline }}" in ["None", "Django Compressor"]:
|
||||
remove_gulp_files()
|
||||
remove_webpack_files()
|
||||
remove_sass_files()
|
||||
remove_packagejson_file()
|
||||
if (
|
||||
"{{ cookiecutter.js_task_runner }}".lower() != "none"
|
||||
and "{{ cookiecutter.use_docker }}".lower() == "y"
|
||||
):
|
||||
print(
|
||||
WARNING
|
||||
+ "Docker and {} JS task runner ".format(
|
||||
"{{ cookiecutter.js_task_runner }}".lower().capitalize()
|
||||
)
|
||||
+ "working together not supported yet. "
|
||||
"You can continue using the generated project like you "
|
||||
"normally would, however you would need to add a JS "
|
||||
"task runner service to your Docker Compose configuration "
|
||||
"manually." + TERMINATOR
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
||||
remove_node_dockerfile()
|
||||
else:
|
||||
handle_js_runner(
|
||||
"{{ cookiecutter.frontend_pipeline }}",
|
||||
use_docker=("{{ cookiecutter.use_docker }}".lower() == "y"),
|
||||
use_async=("{{ cookiecutter.use_async }}".lower() == "y"),
|
||||
)
|
||||
|
||||
if "{{ cookiecutter.cloud_provider }}" == "None" and "{{ cookiecutter.use_docker }}".lower() == "n":
|
||||
print(
|
||||
WARNING + "You chose to not use any cloud providers nor Docker, "
|
||||
"media files won't be served in production." + TERMINATOR
|
||||
)
|
||||
remove_storages_module()
|
||||
|
||||
if "{{ cookiecutter.use_celery }}".lower() == "n":
|
||||
remove_celery_app()
|
||||
remove_celery_files()
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
||||
remove_celery_compose_dirs()
|
||||
|
||||
if "{{ cookiecutter.use_travisci }}".lower() == "n":
|
||||
if "{{ cookiecutter.ci_tool }}" != "Travis":
|
||||
remove_dottravisyml_file()
|
||||
|
||||
if "{{ cookiecutter.ci_tool }}" != "Gitlab":
|
||||
remove_dotgitlabciyml_file()
|
||||
|
||||
if "{{ cookiecutter.ci_tool }}" != "Github":
|
||||
remove_dotgithub_folder()
|
||||
|
||||
if "{{ cookiecutter.use_drf }}".lower() == "n":
|
||||
remove_drf_starter_files()
|
||||
|
||||
if "{{ cookiecutter.use_async }}".lower() == "n":
|
||||
remove_async_files()
|
||||
|
||||
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ NOTE:
|
|||
as the whole Cookiecutter Django project initialization
|
||||
can potentially be run in Python 2.x environment.
|
||||
|
||||
TODO: ? restrict Cookiecutter Django project initialization to Python 3.x environments only
|
||||
TODO: restrict Cookiecutter Django project initialization
|
||||
to Python 3.x environments only
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
|
@ -16,11 +17,19 @@ INFO = "\x1b[1;33m [INFO]: "
|
|||
HINT = "\x1b[3;33m"
|
||||
SUCCESS = "\x1b[1;32m [SUCCESS]: "
|
||||
|
||||
# The content of this string is evaluated by Jinja, and plays an important role.
|
||||
# It updates the cookiecutter context to trim leading and trailing spaces
|
||||
# from domain/email values
|
||||
"""
|
||||
{{ cookiecutter.update({ "domain_name": cookiecutter.domain_name | trim }) }}
|
||||
{{ cookiecutter.update({ "email": cookiecutter.email | trim }) }}
|
||||
"""
|
||||
|
||||
project_slug = "{{ cookiecutter.project_slug }}"
|
||||
if hasattr(project_slug, "isidentifier"):
|
||||
assert project_slug.isidentifier(), "'{}' project slug is not a valid Python identifier.".format(
|
||||
project_slug
|
||||
)
|
||||
assert project_slug.isidentifier(), "'{}' project slug is not a valid Python identifier.".format(project_slug)
|
||||
|
||||
assert project_slug == project_slug.lower(), "'{}' project slug should be all lowercase".format(project_slug)
|
||||
|
||||
assert "\\" not in "{{ cookiecutter.author_name }}", "Don't include backslashes in author name."
|
||||
|
||||
|
@ -28,13 +37,12 @@ if "{{ cookiecutter.use_docker }}".lower() == "n":
|
|||
python_major_version = sys.version_info[0]
|
||||
if python_major_version == 2:
|
||||
print(
|
||||
WARNING + "Cookiecutter Django does not support Python 2. "
|
||||
"Stability is guaranteed with Python 3.6+ only, "
|
||||
"are you sure you want to proceed (y/n)? " + TERMINATOR
|
||||
WARNING + "You're running cookiecutter under Python 2, but the generated "
|
||||
"project requires Python 3.11+. Do you want to proceed (y/n)? " + TERMINATOR
|
||||
)
|
||||
yes_options, no_options = frozenset(["y"]), frozenset(["n"])
|
||||
while True:
|
||||
choice = raw_input().lower()
|
||||
choice = raw_input().lower() # noqa: F821
|
||||
if choice in yes_options:
|
||||
break
|
||||
|
||||
|
@ -45,12 +53,16 @@ if "{{ cookiecutter.use_docker }}".lower() == "n":
|
|||
print(
|
||||
HINT
|
||||
+ "Please respond with {} or {}: ".format(
|
||||
", ".join(
|
||||
["'{}'".format(o) for o in yes_options if not o == ""]
|
||||
),
|
||||
", ".join(
|
||||
["'{}'".format(o) for o in no_options if not o == ""]
|
||||
),
|
||||
", ".join(["'{}'".format(o) for o in yes_options if not o == ""]),
|
||||
", ".join(["'{}'".format(o) for o in no_options if not o == ""]),
|
||||
)
|
||||
+ TERMINATOR
|
||||
)
|
||||
|
||||
if "{{ cookiecutter.use_whitenoise }}".lower() == "n" and "{{ cookiecutter.cloud_provider }}" == "None":
|
||||
print("You should either use Whitenoise or select a " "Cloud Provider to serve static files")
|
||||
sys.exit(1)
|
||||
|
||||
if "{{ cookiecutter.mail_service }}" == "Amazon SES" and "{{ cookiecutter.cloud_provider }}" != "AWS":
|
||||
print("You should either use AWS or select a different " "Mail Service for sending emails.")
|
||||
sys.exit(1)
|
||||
|
|
50
pyproject.toml
Normal file
50
pyproject.toml
Normal file
|
@ -0,0 +1,50 @@
|
|||
# ==== pytest ====
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "-v --tb=short"
|
||||
norecursedirs = [
|
||||
".tox",
|
||||
".git",
|
||||
"*/migrations/*",
|
||||
"*/static/*",
|
||||
"docs",
|
||||
"venv",
|
||||
"*/{{cookiecutter.project_slug}}/*",
|
||||
]
|
||||
|
||||
|
||||
# ==== black ====
|
||||
[tool.black]
|
||||
line-length = 119
|
||||
target-version = ['py311']
|
||||
|
||||
|
||||
# ==== isort ====
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
line_length = 119
|
||||
known_first_party = [
|
||||
"tests",
|
||||
"scripts",
|
||||
"hooks",
|
||||
]
|
||||
|
||||
|
||||
# ==== djLint ====
|
||||
[tool.djlint]
|
||||
blank_line_after_tag = "load,extends"
|
||||
close_void_tags = true
|
||||
format_css = true
|
||||
format_js = true
|
||||
# TODO: remove T002 when fixed https://github.com/Riverside-Healthcare/djLint/issues/687
|
||||
ignore = "H006,H030,H031,T002,T028"
|
||||
ignore_blocks = "raw"
|
||||
include = "H017,H035"
|
||||
indent = 2
|
||||
max_line_length = 119
|
||||
profile = "jinja"
|
||||
|
||||
[tool.djlint.css]
|
||||
indent_size = 2
|
||||
|
||||
[tool.djlint.js]
|
||||
indent_size = 2
|
|
@ -1,3 +0,0 @@
|
|||
[pytest]
|
||||
python_paths = .
|
||||
norecursedirs = .tox .git */migrations/* */static/* docs venv */{{cookiecutter.project_slug}}/*
|
|
@ -1,13 +1,28 @@
|
|||
cookiecutter==1.6.0
|
||||
sh==1.12.14
|
||||
cookiecutter==2.1.1
|
||||
sh==2.0.4; sys_platform != "win32"
|
||||
binaryornot==0.4.4
|
||||
|
||||
# Code quality
|
||||
# ------------------------------------------------------------------------------
|
||||
flake8==3.6.0
|
||||
black==23.3.0
|
||||
isort==5.12.0
|
||||
flake8==6.0.0
|
||||
django-upgrade==1.14.0
|
||||
djlint==1.31.1
|
||||
pre-commit==3.3.3
|
||||
|
||||
# Testing
|
||||
# ------------------------------------------------------------------------------
|
||||
tox==3.6.1
|
||||
pytest==4.1.1
|
||||
pytest-cookies==0.3.0
|
||||
tox==4.6.3
|
||||
pytest==7.4.0
|
||||
pytest-xdist==3.3.1
|
||||
pytest-cookies==0.7.0
|
||||
pytest-instafail==0.5.0
|
||||
pyyaml==6.0
|
||||
|
||||
# Scripting
|
||||
# ------------------------------------------------------------------------------
|
||||
PyGithub==1.59.0
|
||||
gitpython==3.1.31
|
||||
jinja2==3.1.2
|
||||
requests==2.31.0
|
||||
|
|
298
scripts/create_django_issue.py
Normal file
298
scripts/create_django_issue.py
Normal file
|
@ -0,0 +1,298 @@
|
|||
"""
|
||||
Creates an issue that generates a table for dependency checking whether
|
||||
all packages support the latest Django version. "Latest" does not include
|
||||
patches, only comparing major and minor version numbers.
|
||||
|
||||
This script handles when there are multiple Django versions that need
|
||||
to keep up to date.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, NamedTuple
|
||||
|
||||
import requests
|
||||
from github import Github
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from github.Issue import Issue
|
||||
|
||||
CURRENT_FILE = Path(__file__)
|
||||
ROOT = CURRENT_FILE.parents[1]
|
||||
REQUIREMENTS_DIR = ROOT / "{{cookiecutter.project_slug}}" / "requirements"
|
||||
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN", None)
|
||||
GITHUB_REPO = os.getenv("GITHUB_REPOSITORY", None)
|
||||
|
||||
|
||||
class DjVersion(NamedTuple):
|
||||
"""
|
||||
Wrapper to parse, compare and render Django versions.
|
||||
|
||||
Only keeps track on (major, minor) versions, excluding patches and pre-releases.
|
||||
"""
|
||||
|
||||
major: int
|
||||
minor: int
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""To render as string."""
|
||||
return f"{self.major}.{self.minor}"
|
||||
|
||||
@classmethod
|
||||
def parse(cls, version_str: str) -> DjVersion:
|
||||
"""Parse interesting values from the version string."""
|
||||
major, minor, *_ = version_str.split(".")
|
||||
return cls(major=int(major), minor=int(minor))
|
||||
|
||||
@classmethod
|
||||
def parse_to_tuple(cls, version_str: str):
|
||||
version = cls.parse(version_str=version_str)
|
||||
return version.major, version.minor
|
||||
|
||||
|
||||
def get_package_info(package: str) -> dict:
|
||||
"""Get package metadata using PyPI API."""
|
||||
# "django" converts to "Django" on redirect
|
||||
r = requests.get(f"https://pypi.org/pypi/{package}/json", allow_redirects=True)
|
||||
if not r.ok:
|
||||
print(f"Couldn't find package: {package}")
|
||||
sys.exit(1)
|
||||
return r.json()
|
||||
|
||||
|
||||
def get_django_versions() -> Iterable[DjVersion]:
|
||||
"""List all django versions."""
|
||||
django_package_info: dict[str, Any] = get_package_info("django")
|
||||
releases = django_package_info["releases"].keys()
|
||||
for release_str in releases:
|
||||
if release_str.replace(".", "").isdigit():
|
||||
# Exclude pre-releases with non-numeric characters in version
|
||||
yield DjVersion.parse(release_str)
|
||||
|
||||
|
||||
def get_name_and_version(requirements_line: str) -> tuple[str, ...]:
|
||||
"""Get the name a version of a package from a line in the requirement file."""
|
||||
full_name, version = requirements_line.split(" ", 1)[0].split("==")
|
||||
name_without_extras = full_name.split("[", 1)[0]
|
||||
return name_without_extras, version
|
||||
|
||||
|
||||
def get_all_latest_django_versions(
|
||||
django_max_version: tuple[DjVersion] = None,
|
||||
) -> tuple[DjVersion, list[DjVersion]]:
|
||||
"""
|
||||
Grabs all Django versions that are worthy of a GitHub issue.
|
||||
Depends on Django versions having higher major version or minor version.
|
||||
"""
|
||||
_django_max_version = (99, 99)
|
||||
if django_max_version:
|
||||
_django_max_version = django_max_version
|
||||
|
||||
print("Fetching all Django versions from PyPI")
|
||||
base_txt = REQUIREMENTS_DIR / "base.txt"
|
||||
with base_txt.open() as f:
|
||||
for line in f.readlines():
|
||||
if "django==" in line.lower():
|
||||
break
|
||||
else:
|
||||
print(f"django not found in {base_txt}") # Huh...?
|
||||
sys.exit(1)
|
||||
|
||||
# Begin parsing and verification
|
||||
_, current_version_str = get_name_and_version(line)
|
||||
# Get a tuple of (major, minor) - ignoring patch version
|
||||
current_minor_version = DjVersion.parse(current_version_str)
|
||||
newer_versions: set[DjVersion] = set()
|
||||
for django_version in get_django_versions():
|
||||
if current_minor_version < django_version <= _django_max_version:
|
||||
newer_versions.add(django_version)
|
||||
|
||||
return current_minor_version, sorted(newer_versions, reverse=True)
|
||||
|
||||
|
||||
_TABLE_HEADER = """
|
||||
|
||||
## {file}.txt
|
||||
|
||||
| Name | Version in Master | {dj_version} Compatible Version | OK |
|
||||
| ---- | :---------------: | :-----------------------------: | :-: |
|
||||
"""
|
||||
VITAL_BUT_UNKNOWN = [
|
||||
"django-environ", # not updated often
|
||||
]
|
||||
|
||||
|
||||
class GitHubManager:
|
||||
def __init__(self, base_dj_version: DjVersion, needed_dj_versions: list[DjVersion]):
|
||||
self.github = Github(GITHUB_TOKEN)
|
||||
self.repo = self.github.get_repo(GITHUB_REPO)
|
||||
|
||||
self.base_dj_version = base_dj_version
|
||||
self.needed_dj_versions = needed_dj_versions
|
||||
# (major+minor) Version and description
|
||||
self.existing_issues: dict[DjVersion, Issue] = {}
|
||||
|
||||
# Load all requirements from our requirements files and preload their
|
||||
# package information like a cache:
|
||||
self.requirements_files = ["base", "local", "production"]
|
||||
# Format:
|
||||
# requirement file name: {package name: (master_version, package_info)}
|
||||
self.requirements: dict[str, dict[str, tuple[str, dict]]] = {x: {} for x in self.requirements_files}
|
||||
|
||||
def setup(self) -> None:
|
||||
self.load_requirements()
|
||||
self.load_existing_issues()
|
||||
|
||||
def load_requirements(self):
|
||||
print("Reading requirements")
|
||||
for requirements_file in self.requirements_files:
|
||||
with (REQUIREMENTS_DIR / f"{requirements_file}.txt").open() as f:
|
||||
for line in f.readlines():
|
||||
if (
|
||||
"==" in line
|
||||
and not line.startswith("{%")
|
||||
and not line.startswith(" #")
|
||||
and not line.startswith("#")
|
||||
and not line.startswith(" ")
|
||||
):
|
||||
name, version = get_name_and_version(line)
|
||||
self.requirements[requirements_file][name] = (
|
||||
version,
|
||||
get_package_info(name),
|
||||
)
|
||||
|
||||
def load_existing_issues(self):
|
||||
"""Closes the issue if the base Django version is greater than needed"""
|
||||
print("Load existing issues from GitHub")
|
||||
qualifiers = {
|
||||
"repo": GITHUB_REPO,
|
||||
"author": "app/github-actions",
|
||||
"state": "open",
|
||||
"is": "issue",
|
||||
"in": "title",
|
||||
}
|
||||
issues = list(self.github.search_issues("[Django Update]", "created", "desc", **qualifiers))
|
||||
print(f"Found {len(issues)} issues matching search")
|
||||
for issue in issues:
|
||||
matches = re.match(r"\[Update Django] Django (\d+.\d+)$", issue.title)
|
||||
if not matches:
|
||||
continue
|
||||
issue_version = DjVersion.parse(matches.group(1))
|
||||
if self.base_dj_version > issue_version:
|
||||
issue.edit(state="closed")
|
||||
print(f"Closed issue {issue.title} (ID: [{issue.id}]({issue.url}))")
|
||||
else:
|
||||
self.existing_issues[issue_version] = issue
|
||||
|
||||
def get_compatibility(self, package_name: str, package_info: dict, needed_dj_version: DjVersion):
|
||||
"""
|
||||
Verify compatibility via setup.py classifiers. If Django is not in the
|
||||
classifiers, then default compatibility is n/a and OK is ✅.
|
||||
|
||||
If it's a package that's vital but known to not be updated often, we give it
|
||||
a ❓. If a package has ❓ or 🕒, then we allow manual update. Automatic updates
|
||||
only include ❌ and ✅.
|
||||
"""
|
||||
# If issue previously existed, find package and skip any gtg, manually
|
||||
# updated packages, or known releases that will happen but haven't yet
|
||||
if issue := self.existing_issues.get(needed_dj_version):
|
||||
if index := issue.body.find(package_name):
|
||||
name, _current, prev_compat, ok = (s.strip() for s in issue.body[index:].split("|", 4)[:4])
|
||||
if ok in ("✅", "❓", "🕒"):
|
||||
return prev_compat, ok
|
||||
|
||||
if package_name in VITAL_BUT_UNKNOWN:
|
||||
return "", "❓"
|
||||
|
||||
# Check classifiers if it includes Django
|
||||
supported_dj_versions: list[DjVersion] = []
|
||||
for classifier in package_info["info"]["classifiers"]:
|
||||
# Usually in the form of "Framework :: Django :: 3.2"
|
||||
tokens = classifier.split(" ")
|
||||
if len(tokens) >= 5 and tokens[2].lower() == "django":
|
||||
version = DjVersion.parse(tokens[4])
|
||||
if len(version) == 2:
|
||||
supported_dj_versions.append(version)
|
||||
|
||||
if supported_dj_versions:
|
||||
if any(v >= needed_dj_version for v in supported_dj_versions):
|
||||
return package_info["info"]["version"], "✅"
|
||||
else:
|
||||
return "", "❌"
|
||||
|
||||
# Django classifier DNE; assume it isn't a Django lib
|
||||
# Great exceptions include pylint-django, where we need to do this manually...
|
||||
return "n/a", "✅"
|
||||
|
||||
HOME_PAGE_URL_KEYS = [
|
||||
"home_page",
|
||||
"project_url",
|
||||
"docs_url",
|
||||
"package_url",
|
||||
"release_url",
|
||||
"bugtrack_url",
|
||||
]
|
||||
|
||||
def _get_md_home_page_url(self, package_info: dict):
|
||||
urls = [package_info["info"].get(url_key) for url_key in self.HOME_PAGE_URL_KEYS]
|
||||
try:
|
||||
return f"[{{}}]({next(item for item in urls if item)})"
|
||||
except StopIteration:
|
||||
return "{}"
|
||||
|
||||
def generate_markdown(self, needed_dj_version: DjVersion):
|
||||
requirements = f"{needed_dj_version} requirements tables\n\n"
|
||||
for _file in self.requirements_files:
|
||||
requirements += _TABLE_HEADER.format_map({"file": _file, "dj_version": needed_dj_version})
|
||||
for package_name, (version, info) in self.requirements[_file].items():
|
||||
compat_version, icon = self.get_compatibility(package_name, info, needed_dj_version)
|
||||
requirements += (
|
||||
f"| {self._get_md_home_page_url(info).format(package_name)} "
|
||||
f"| {version.strip()} "
|
||||
f"| {compat_version.strip()} "
|
||||
f"| {icon} "
|
||||
f"|\n"
|
||||
)
|
||||
|
||||
return requirements
|
||||
|
||||
def create_or_edit_issue(self, needed_dj_version: DjVersion, description: str):
|
||||
if issue := self.existing_issues.get(needed_dj_version):
|
||||
print(f"Editing issue #{issue.number} for Django {needed_dj_version}")
|
||||
issue.edit(body=description)
|
||||
else:
|
||||
print(f"Creating new issue for Django {needed_dj_version}")
|
||||
issue = self.repo.create_issue(f"[Update Django] Django {needed_dj_version}", description)
|
||||
issue.add_to_labels(f"django{needed_dj_version}")
|
||||
|
||||
def generate(self):
|
||||
for version in self.needed_dj_versions:
|
||||
print(f"Handling GitHub issue for Django {version}")
|
||||
md_content = self.generate_markdown(version)
|
||||
print(f"Generated markdown:\n\n{md_content}")
|
||||
self.create_or_edit_issue(version, md_content)
|
||||
|
||||
|
||||
def main(django_max_version=None) -> None:
|
||||
# Check if there are any djs
|
||||
current_dj, latest_djs = get_all_latest_django_versions(django_max_version=django_max_version)
|
||||
if not latest_djs:
|
||||
sys.exit(0)
|
||||
manager = GitHubManager(current_dj, latest_djs)
|
||||
manager.setup()
|
||||
manager.generate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if GITHUB_REPO is None:
|
||||
raise RuntimeError("No github repo, please set the environment variable GITHUB_REPOSITORY")
|
||||
max_version = None
|
||||
last_arg = sys.argv[-1]
|
||||
if CURRENT_FILE.name not in last_arg:
|
||||
max_version = DjVersion.parse_to_tuple(version_str=last_arg)
|
||||
|
||||
main(django_max_version=max_version)
|
160
scripts/update_changelog.py
Normal file
160
scripts/update_changelog.py
Normal file
|
@ -0,0 +1,160 @@
|
|||
import datetime as dt
|
||||
import os
|
||||
import re
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
import git
|
||||
import github.PullRequest
|
||||
import github.Repository
|
||||
from github import Github
|
||||
from jinja2 import Template
|
||||
|
||||
CURRENT_FILE = Path(__file__)
|
||||
ROOT = CURRENT_FILE.parents[1]
|
||||
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
|
||||
GITHUB_REPO = os.getenv("GITHUB_REPOSITORY")
|
||||
GIT_BRANCH = os.getenv("GITHUB_REF_NAME")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Script entry point.
|
||||
"""
|
||||
# Generate changelog for PRs merged yesterday
|
||||
merged_date = dt.date.today() - dt.timedelta(days=1)
|
||||
repo = Github(login_or_token=GITHUB_TOKEN).get_repo(GITHUB_REPO)
|
||||
merged_pulls = list(iter_pulls(repo, merged_date))
|
||||
print(f"Merged pull requests: {merged_pulls}")
|
||||
if not merged_pulls:
|
||||
print("Nothing was merged, existing.")
|
||||
return
|
||||
|
||||
# Group pull requests by type of change
|
||||
grouped_pulls = group_pulls_by_change_type(merged_pulls)
|
||||
|
||||
# Generate portion of markdown
|
||||
release_changes_summary = generate_md(grouped_pulls)
|
||||
print(f"Summary of changes: {release_changes_summary}")
|
||||
|
||||
# Update CHANGELOG.md file
|
||||
release = f"{merged_date:%Y.%m.%d}"
|
||||
changelog_path = ROOT / "CHANGELOG.md"
|
||||
write_changelog(changelog_path, release, release_changes_summary)
|
||||
print(f"Wrote {changelog_path}")
|
||||
|
||||
# Update version
|
||||
setup_py_path = ROOT / "setup.py"
|
||||
update_version(setup_py_path, release)
|
||||
print(f"Updated version in {setup_py_path}")
|
||||
|
||||
# Commit changes, create tag and push
|
||||
update_git_repo([changelog_path, setup_py_path], release)
|
||||
|
||||
# Create GitHub release
|
||||
github_release = repo.create_git_release(
|
||||
tag=release,
|
||||
name=release,
|
||||
message=release_changes_summary,
|
||||
)
|
||||
print(f"Created release on GitHub {github_release}")
|
||||
|
||||
|
||||
def iter_pulls(
|
||||
repo: github.Repository.Repository,
|
||||
merged_date: dt.date,
|
||||
) -> Iterable[github.PullRequest.PullRequest]:
|
||||
"""Fetch merged pull requests at the date we're interested in."""
|
||||
recent_pulls = repo.get_pulls(
|
||||
state="closed",
|
||||
sort="updated",
|
||||
direction="desc",
|
||||
).get_page(0)
|
||||
for pull in recent_pulls:
|
||||
if pull.merged and pull.merged_at.date() == merged_date:
|
||||
yield pull
|
||||
|
||||
|
||||
def group_pulls_by_change_type(
|
||||
pull_requests_list: list[github.PullRequest.PullRequest],
|
||||
) -> dict[str, list[github.PullRequest.PullRequest]]:
|
||||
"""Group pull request by change type."""
|
||||
grouped_pulls = {
|
||||
"Changed": [],
|
||||
"Fixed": [],
|
||||
"Documentation": [],
|
||||
"Updated": [],
|
||||
}
|
||||
for pull in pull_requests_list:
|
||||
label_names = {label.name for label in pull.labels}
|
||||
if "project infrastructure" in label_names:
|
||||
# Don't mention it in the changelog
|
||||
continue
|
||||
if "update" in label_names:
|
||||
group_name = "Updated"
|
||||
elif "bug" in label_names:
|
||||
group_name = "Fixed"
|
||||
elif "docs" in label_names:
|
||||
group_name = "Documentation"
|
||||
else:
|
||||
group_name = "Changed"
|
||||
grouped_pulls[group_name].append(pull)
|
||||
return grouped_pulls
|
||||
|
||||
|
||||
def generate_md(grouped_pulls: dict[str, list[github.PullRequest.PullRequest]]) -> str:
|
||||
"""Generate markdown file from Jinja template."""
|
||||
changelog_template = ROOT / ".github" / "changelog-template.md"
|
||||
template = Template(changelog_template.read_text(), autoescape=True)
|
||||
return template.render(grouped_pulls=grouped_pulls)
|
||||
|
||||
|
||||
def write_changelog(file_path: Path, release: str, content: str) -> None:
|
||||
"""Write Release details to the changelog file."""
|
||||
content = f"## {release}\n{content}"
|
||||
old_content = file_path.read_text()
|
||||
updated_content = old_content.replace(
|
||||
"<!-- GENERATOR_PLACEHOLDER -->",
|
||||
f"<!-- GENERATOR_PLACEHOLDER -->\n\n{content}",
|
||||
)
|
||||
file_path.write_text(updated_content)
|
||||
|
||||
|
||||
def update_version(file_path: Path, release: str) -> None:
|
||||
"""Update template version in setup.py."""
|
||||
old_content = file_path.read_text()
|
||||
updated_content = re.sub(
|
||||
r'\nversion = "\d+\.\d+\.\d+"\n',
|
||||
f'\nversion = "{release}"\n',
|
||||
old_content,
|
||||
)
|
||||
file_path.write_text(updated_content)
|
||||
|
||||
|
||||
def update_git_repo(paths: list[Path], release: str) -> None:
|
||||
"""Commit, tag changes in git repo and push to origin."""
|
||||
repo = git.Repo(ROOT)
|
||||
for path in paths:
|
||||
repo.git.add(path)
|
||||
message = f"Release {release}"
|
||||
|
||||
user = repo.git.config("--get", "user.name")
|
||||
email = repo.git.config("--get", "user.email")
|
||||
|
||||
repo.git.commit(
|
||||
m=message,
|
||||
author=f"{user} <{email}>",
|
||||
)
|
||||
repo.git.tag("-a", release, m=message)
|
||||
server = f"https://{GITHUB_TOKEN}@github.com/{GITHUB_REPO}.git"
|
||||
print(f"Pushing changes to {GIT_BRANCH} branch of {GITHUB_REPO}")
|
||||
repo.git.push(server, GIT_BRANCH)
|
||||
repo.git.push("--tags", server, GIT_BRANCH)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if GITHUB_REPO is None:
|
||||
raise RuntimeError("No github repo, please set the environment variable GITHUB_REPOSITORY")
|
||||
if GIT_BRANCH is None:
|
||||
raise RuntimeError("No git branch set, please set the GITHUB_REF_NAME environment variable")
|
||||
main()
|
102
scripts/update_contributors.py
Normal file
102
scripts/update_contributors.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from github import Github
|
||||
from github.NamedUser import NamedUser
|
||||
from jinja2 import Template
|
||||
|
||||
CURRENT_FILE = Path(__file__)
|
||||
ROOT = CURRENT_FILE.parents[1]
|
||||
BOT_LOGINS = ["pyup-bot"]
|
||||
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN", None)
|
||||
GITHUB_REPO = os.getenv("GITHUB_REPOSITORY", None)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Script entry point.
|
||||
|
||||
1. Fetch recent contributors from the Github API
|
||||
2. Add missing ones to the JSON file
|
||||
3. Generate Markdown from JSON file
|
||||
"""
|
||||
recent_authors = set(iter_recent_authors())
|
||||
|
||||
# Add missing users to the JSON file
|
||||
contrib_file = ContributorsJSONFile()
|
||||
for author in recent_authors:
|
||||
print(f"Checking if {author.login} should be added")
|
||||
if author.login not in contrib_file:
|
||||
contrib_file.add_contributor(author)
|
||||
print(f"Added {author.login} to contributors")
|
||||
contrib_file.save()
|
||||
|
||||
# Generate MD file from JSON file
|
||||
write_md_file(contrib_file.content)
|
||||
|
||||
|
||||
def iter_recent_authors():
|
||||
"""
|
||||
Fetch users who opened recently merged pull requests.
|
||||
|
||||
Use Github API to fetch recent authors rather than
|
||||
git CLI to work with Github usernames.
|
||||
"""
|
||||
repo = Github(login_or_token=GITHUB_TOKEN, per_page=5).get_repo(GITHUB_REPO)
|
||||
recent_pulls = repo.get_pulls(state="closed", sort="updated", direction="desc").get_page(0)
|
||||
for pull in recent_pulls:
|
||||
if pull.merged and pull.user.type == "User" and pull.user.login not in BOT_LOGINS:
|
||||
yield pull.user
|
||||
|
||||
|
||||
class ContributorsJSONFile:
|
||||
"""Helper to interact with the JSON file."""
|
||||
|
||||
file_path = ROOT / ".github" / "contributors.json"
|
||||
content = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Read initial content."""
|
||||
self.content = json.loads(self.file_path.read_text())
|
||||
|
||||
def __contains__(self, github_login: str):
|
||||
"""Provide a nice API to do: `username in file`."""
|
||||
return any(
|
||||
# Github usernames are case insensitive
|
||||
github_login.lower() == contrib["github_login"].lower()
|
||||
for contrib in self.content
|
||||
)
|
||||
|
||||
def add_contributor(self, user: NamedUser):
|
||||
"""Append the contributor data we care about at the end."""
|
||||
contributor_data = {
|
||||
"name": user.name or user.login,
|
||||
"github_login": user.login,
|
||||
"twitter_username": user.twitter_username or "",
|
||||
}
|
||||
self.content.append(contributor_data)
|
||||
|
||||
def save(self):
|
||||
"""Write the file to disk with indentation."""
|
||||
text_content = json.dumps(self.content, indent=2, ensure_ascii=False)
|
||||
self.file_path.write_text(text_content)
|
||||
|
||||
|
||||
def write_md_file(contributors):
|
||||
"""Generate markdown file from Jinja template."""
|
||||
contributors_template = ROOT / ".github" / "CONTRIBUTORS-template.md"
|
||||
template = Template(contributors_template.read_text(), autoescape=True)
|
||||
core_contributors = [c for c in contributors if c.get("is_core", False)]
|
||||
other_contributors = (c for c in contributors if not c.get("is_core", False))
|
||||
other_contributors = sorted(other_contributors, key=lambda c: c["name"].lower())
|
||||
content = template.render(core_contributors=core_contributors, other_contributors=other_contributors)
|
||||
|
||||
file_path = ROOT / "CONTRIBUTORS.md"
|
||||
file_path.write_text(content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if GITHUB_REPO is None:
|
||||
raise RuntimeError("No github repo, please set the environment variable GITHUB_REPOSITORY")
|
||||
main()
|
22
setup.py
22
setup.py
|
@ -1,21 +1,11 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
# Our version ALWAYS matches the version of Django we support
|
||||
# If Django has a new release, we branch, tag, then update this setting after the tag.
|
||||
version = "2.0.2"
|
||||
|
||||
if sys.argv[-1] == "tag":
|
||||
os.system('git tag -a %s -m "version %s"' % (version, version))
|
||||
os.system("git push --tags")
|
||||
sys.exit()
|
||||
# We use calendar versioning
|
||||
version = "2023.06.27"
|
||||
|
||||
with open("README.rst") as readme_file:
|
||||
long_description = readme_file.read()
|
||||
|
@ -23,24 +13,24 @@ with open("README.rst") as readme_file:
|
|||
setup(
|
||||
name="cookiecutter-django",
|
||||
version=version,
|
||||
description="A Cookiecutter template for creating production-ready Django projects quickly.",
|
||||
description=("A Cookiecutter template for creating production-ready " "Django projects quickly."),
|
||||
long_description=long_description,
|
||||
author="Daniel Roy Greenfeld",
|
||||
author_email="pydanny@gmail.com",
|
||||
url="https://github.com/pydanny/cookiecutter-django",
|
||||
url="https://github.com/cookiecutter/cookiecutter-django",
|
||||
packages=[],
|
||||
license="BSD",
|
||||
zip_safe=False,
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Framework :: Django :: 2.0",
|
||||
"Framework :: Django :: 4.2",
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: English",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Topic :: Software Development",
|
||||
],
|
||||
|
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
37
tests/test_bare.sh
Executable file
37
tests/test_bare.sh
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/bin/sh
|
||||
# this is a very simple script that tests the docker configuration for cookiecutter-django
|
||||
# it is meant to be run from the root directory of the repository, eg:
|
||||
# sh tests/test_bare.sh
|
||||
|
||||
set -o errexit
|
||||
set -x
|
||||
|
||||
# create a cache directory
|
||||
mkdir -p .cache/bare
|
||||
cd .cache/bare
|
||||
|
||||
# create the project using the default settings in cookiecutter.json
|
||||
cookiecutter ../../ --no-input --overwrite-if-exists use_docker=n "$@"
|
||||
cd my_awesome_project
|
||||
|
||||
# Install OS deps
|
||||
sudo utility/install_os_dependencies.sh install
|
||||
|
||||
# Install Python deps
|
||||
pip install -r requirements/local.txt
|
||||
|
||||
# run the project's tests
|
||||
pytest
|
||||
|
||||
# Make sure the check doesn't raise any warnings
|
||||
python manage.py check --fail-level WARNING
|
||||
|
||||
# Run npm build script if package.json is present
|
||||
if [ -f "package.json" ]
|
||||
then
|
||||
npm install
|
||||
npm run build
|
||||
fi
|
||||
|
||||
# Generate the HTML for the documentation
|
||||
cd docs && make html
|
|
@ -1,13 +1,32 @@
|
|||
import glob
|
||||
import os
|
||||
import re
|
||||
import sh
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from binaryornot.check import is_binary
|
||||
|
||||
PATTERN = "{{(\s?cookiecutter)[.](.*?)}}"
|
||||
try:
|
||||
import sh
|
||||
except (ImportError, ModuleNotFoundError):
|
||||
sh = None # sh doesn't support Windows
|
||||
import yaml
|
||||
from binaryornot.check import is_binary
|
||||
from cookiecutter.exceptions import FailedHookException
|
||||
|
||||
PATTERN = r"{{(\s?cookiecutter)[.](.*?)}}"
|
||||
RE_OBJ = re.compile(PATTERN)
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
pytest.skip("sh doesn't support windows", allow_module_level=True)
|
||||
elif sys.platform.startswith("darwin") and os.getenv("CI"):
|
||||
pytest.skip("skipping slow macOS tests on CI", allow_module_level=True)
|
||||
|
||||
# Run auto-fixable styles checks - skipped on CI by default. These can be fixed
|
||||
# automatically by running pre-commit after generation however they are tedious
|
||||
# to fix in the template, so we don't insist too much in fixing them.
|
||||
AUTOFIXABLE_STYLES = os.getenv("AUTOFIXABLE_STYLES") == "1"
|
||||
auto_fixable = pytest.mark.skipif(not AUTOFIXABLE_STYLES, reason="auto-fixable")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def context():
|
||||
|
@ -23,65 +42,367 @@ def context():
|
|||
}
|
||||
|
||||
|
||||
def build_files_list(root_dir):
|
||||
SUPPORTED_COMBINATIONS = [
|
||||
{"username_type": "username"},
|
||||
{"username_type": "email"},
|
||||
{"open_source_license": "MIT"},
|
||||
{"open_source_license": "BSD"},
|
||||
{"open_source_license": "GPLv3"},
|
||||
{"open_source_license": "Apache Software License 2.0"},
|
||||
{"open_source_license": "Not open source"},
|
||||
{"windows": "y"},
|
||||
{"windows": "n"},
|
||||
{"use_pycharm": "y"},
|
||||
{"use_pycharm": "n"},
|
||||
{"use_docker": "y"},
|
||||
{"use_docker": "n"},
|
||||
{"postgresql_version": "14"},
|
||||
{"postgresql_version": "13"},
|
||||
{"postgresql_version": "12"},
|
||||
{"postgresql_version": "11"},
|
||||
{"postgresql_version": "10"},
|
||||
{"cloud_provider": "AWS", "use_whitenoise": "y"},
|
||||
{"cloud_provider": "AWS", "use_whitenoise": "n"},
|
||||
{"cloud_provider": "GCP", "use_whitenoise": "y"},
|
||||
{"cloud_provider": "GCP", "use_whitenoise": "n"},
|
||||
{"cloud_provider": "Azure", "use_whitenoise": "y"},
|
||||
{"cloud_provider": "Azure", "use_whitenoise": "n"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Mailgun"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Mailjet"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Mandrill"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Postmark"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Sendgrid"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "SendinBlue"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "SparkPost"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Other SMTP"},
|
||||
# Note: cloud_provider=None AND use_whitenoise=n is not supported
|
||||
{"cloud_provider": "AWS", "mail_service": "Mailgun"},
|
||||
{"cloud_provider": "AWS", "mail_service": "Amazon SES"},
|
||||
{"cloud_provider": "AWS", "mail_service": "Mailjet"},
|
||||
{"cloud_provider": "AWS", "mail_service": "Mandrill"},
|
||||
{"cloud_provider": "AWS", "mail_service": "Postmark"},
|
||||
{"cloud_provider": "AWS", "mail_service": "Sendgrid"},
|
||||
{"cloud_provider": "AWS", "mail_service": "SendinBlue"},
|
||||
{"cloud_provider": "AWS", "mail_service": "SparkPost"},
|
||||
{"cloud_provider": "AWS", "mail_service": "Other SMTP"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Mailgun"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Mailjet"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Mandrill"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Postmark"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Sendgrid"},
|
||||
{"cloud_provider": "GCP", "mail_service": "SendinBlue"},
|
||||
{"cloud_provider": "GCP", "mail_service": "SparkPost"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Other SMTP"},
|
||||
{"cloud_provider": "Azure", "mail_service": "Mailgun"},
|
||||
{"cloud_provider": "Azure", "mail_service": "Mailjet"},
|
||||
{"cloud_provider": "Azure", "mail_service": "Mandrill"},
|
||||
{"cloud_provider": "Azure", "mail_service": "Postmark"},
|
||||
{"cloud_provider": "Azure", "mail_service": "Sendgrid"},
|
||||
{"cloud_provider": "Azure", "mail_service": "SendinBlue"},
|
||||
{"cloud_provider": "Azure", "mail_service": "SparkPost"},
|
||||
{"cloud_provider": "Azure", "mail_service": "Other SMTP"},
|
||||
# Note: cloud_providers GCP, Azure, and None
|
||||
# with mail_service Amazon SES is not supported
|
||||
{"use_async": "y"},
|
||||
{"use_async": "n"},
|
||||
{"use_drf": "y"},
|
||||
{"use_drf": "n"},
|
||||
{"frontend_pipeline": "None"},
|
||||
{"frontend_pipeline": "Django Compressor"},
|
||||
{"frontend_pipeline": "Gulp"},
|
||||
{"frontend_pipeline": "Webpack"},
|
||||
{"use_celery": "y"},
|
||||
{"use_celery": "n"},
|
||||
{"use_mailhog": "y"},
|
||||
{"use_mailhog": "n"},
|
||||
{"use_sentry": "y"},
|
||||
{"use_sentry": "n"},
|
||||
{"use_whitenoise": "y"},
|
||||
{"use_whitenoise": "n"},
|
||||
{"use_heroku": "y"},
|
||||
{"use_heroku": "n"},
|
||||
{"ci_tool": "None"},
|
||||
{"ci_tool": "Travis"},
|
||||
{"ci_tool": "Gitlab"},
|
||||
{"ci_tool": "Github"},
|
||||
{"keep_local_envs_in_vcs": "y"},
|
||||
{"keep_local_envs_in_vcs": "n"},
|
||||
{"debug": "y"},
|
||||
{"debug": "n"},
|
||||
]
|
||||
|
||||
UNSUPPORTED_COMBINATIONS = [
|
||||
{"cloud_provider": "None", "use_whitenoise": "n"},
|
||||
{"cloud_provider": "GCP", "mail_service": "Amazon SES"},
|
||||
{"cloud_provider": "Azure", "mail_service": "Amazon SES"},
|
||||
{"cloud_provider": "None", "mail_service": "Amazon SES"},
|
||||
]
|
||||
|
||||
|
||||
def _fixture_id(ctx):
|
||||
"""Helper to get a user-friendly test name from the parametrized context."""
|
||||
return "-".join(f"{key}:{value}" for key, value in ctx.items())
|
||||
|
||||
|
||||
def build_files_list(base_dir):
|
||||
"""Build a list containing absolute paths to the generated files."""
|
||||
return [
|
||||
os.path.join(dirpath, file_path)
|
||||
for dirpath, subdirs, files in os.walk(root_dir)
|
||||
for file_path in files
|
||||
]
|
||||
return [os.path.join(dirpath, file_path) for dirpath, subdirs, files in os.walk(base_dir) for file_path in files]
|
||||
|
||||
|
||||
def check_paths(paths):
|
||||
"""Method to check all paths have correct substitutions,
|
||||
used by other tests cases
|
||||
"""
|
||||
"""Method to check all paths have correct substitutions."""
|
||||
# Assert that no match is found in any of the files
|
||||
for path in paths:
|
||||
if is_binary(path):
|
||||
continue
|
||||
|
||||
for line in open(path, "r"):
|
||||
for line in open(path):
|
||||
match = RE_OBJ.search(line)
|
||||
msg = "cookiecutter variable not replaced in {}"
|
||||
assert match is None, msg.format(path)
|
||||
assert match is None, f"cookiecutter variable not replaced in {path}"
|
||||
|
||||
|
||||
def test_default_configuration(cookies, context):
|
||||
result = cookies.bake(extra_context=context)
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_project_generation(cookies, context, context_override):
|
||||
"""Test that project is generated and fully rendered."""
|
||||
|
||||
result = cookies.bake(extra_context={**context, **context_override})
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project.basename == context["project_slug"]
|
||||
assert result.project.isdir()
|
||||
assert result.project_path.name == context["project_slug"]
|
||||
assert result.project_path.is_dir()
|
||||
|
||||
paths = build_files_list(str(result.project))
|
||||
paths = build_files_list(str(result.project_path))
|
||||
assert paths
|
||||
check_paths(paths)
|
||||
|
||||
|
||||
@pytest.fixture(params=["use_mailhog", "use_celery", "windows"])
|
||||
def feature_context(request, context):
|
||||
context.update({request.param: "y"})
|
||||
return context
|
||||
|
||||
|
||||
def test_enabled_features(cookies, feature_context):
|
||||
result = cookies.bake(extra_context=feature_context)
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project.basename == feature_context["project_slug"]
|
||||
assert result.project.isdir()
|
||||
|
||||
paths = build_files_list(str(result.project))
|
||||
assert paths
|
||||
check_paths(paths)
|
||||
|
||||
|
||||
def test_flake8_compliance(cookies):
|
||||
"""generated project should pass flake8"""
|
||||
result = cookies.bake()
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_flake8_passes(cookies, context_override):
|
||||
"""Generated project should pass flake8."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.flake8(str(result.project))
|
||||
sh.flake8(_cwd=str(result.project_path))
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@auto_fixable
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_black_passes(cookies, context_override):
|
||||
"""Check whether generated project passes black style."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.black(
|
||||
"--check",
|
||||
"--diff",
|
||||
"--exclude",
|
||||
"migrations",
|
||||
".",
|
||||
_cwd=str(result.project_path),
|
||||
)
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@auto_fixable
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_isort_passes(cookies, context_override):
|
||||
"""Check whether generated project passes isort style."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.isort(_cwd=str(result.project_path))
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@auto_fixable
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_django_upgrade_passes(cookies, context_override):
|
||||
"""Check whether generated project passes django-upgrade."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
python_files = [
|
||||
file_path.removeprefix(f"{result.project_path}/")
|
||||
for file_path in glob.glob(str(result.project_path / "**" / "*.py"), recursive=True)
|
||||
]
|
||||
try:
|
||||
sh.django_upgrade(
|
||||
"--target-version",
|
||||
"4.2",
|
||||
*python_files,
|
||||
_cwd=str(result.project_path),
|
||||
)
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_djlint_lint_passes(cookies, context_override):
|
||||
"""Check whether generated project passes djLint --lint."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
autofixable_rules = "H014,T001"
|
||||
# TODO: remove T002 when fixed https://github.com/Riverside-Healthcare/djLint/issues/687
|
||||
ignored_rules = "H006,H030,H031,T002"
|
||||
try:
|
||||
sh.djlint("--lint", "--ignore", f"{autofixable_rules},{ignored_rules}", ".", _cwd=str(result.project_path))
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@auto_fixable
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_djlint_check_passes(cookies, context_override):
|
||||
"""Check whether generated project passes djLint --check."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.djlint("--check", ".", _cwd=str(result.project_path))
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["use_docker", "expected_test_script"],
|
||||
[
|
||||
("n", "pytest"),
|
||||
("y", "docker-compose -f local.yml run django pytest"),
|
||||
],
|
||||
)
|
||||
def test_travis_invokes_pytest(cookies, context, use_docker, expected_test_script):
|
||||
context.update({"ci_tool": "Travis", "use_docker": use_docker})
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project_path.name == context["project_slug"]
|
||||
assert result.project_path.is_dir()
|
||||
|
||||
with open(f"{result.project_path}/.travis.yml") as travis_yml:
|
||||
try:
|
||||
yml = yaml.safe_load(travis_yml)["jobs"]["include"]
|
||||
assert yml[0]["script"] == ["flake8"]
|
||||
assert yml[1]["script"] == [expected_test_script]
|
||||
except yaml.YAMLError as e:
|
||||
pytest.fail(str(e))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["use_docker", "expected_test_script"],
|
||||
[
|
||||
("n", "pytest"),
|
||||
("y", "docker-compose -f local.yml run django pytest"),
|
||||
],
|
||||
)
|
||||
def test_gitlab_invokes_precommit_and_pytest(cookies, context, use_docker, expected_test_script):
|
||||
context.update({"ci_tool": "Gitlab", "use_docker": use_docker})
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project_path.name == context["project_slug"]
|
||||
assert result.project_path.is_dir()
|
||||
|
||||
with open(f"{result.project_path}/.gitlab-ci.yml") as gitlab_yml:
|
||||
try:
|
||||
gitlab_config = yaml.safe_load(gitlab_yml)
|
||||
assert gitlab_config["precommit"]["script"] == [
|
||||
"pre-commit run --show-diff-on-failure --color=always --all-files"
|
||||
]
|
||||
assert gitlab_config["pytest"]["script"] == [expected_test_script]
|
||||
except yaml.YAMLError as e:
|
||||
pytest.fail(e)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["use_docker", "expected_test_script"],
|
||||
[
|
||||
("n", "pytest"),
|
||||
("y", "docker-compose -f local.yml run django pytest"),
|
||||
],
|
||||
)
|
||||
def test_github_invokes_linter_and_pytest(cookies, context, use_docker, expected_test_script):
|
||||
context.update({"ci_tool": "Github", "use_docker": use_docker})
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project_path.name == context["project_slug"]
|
||||
assert result.project_path.is_dir()
|
||||
|
||||
with open(f"{result.project_path}/.github/workflows/ci.yml") as github_yml:
|
||||
try:
|
||||
github_config = yaml.safe_load(github_yml)
|
||||
linter_present = False
|
||||
for action_step in github_config["jobs"]["linter"]["steps"]:
|
||||
if action_step.get("uses", "NA").startswith("pre-commit"):
|
||||
linter_present = True
|
||||
assert linter_present
|
||||
|
||||
expected_test_script_present = False
|
||||
for action_step in github_config["jobs"]["pytest"]["steps"]:
|
||||
if action_step.get("run") == expected_test_script:
|
||||
expected_test_script_present = True
|
||||
assert expected_test_script_present
|
||||
except yaml.YAMLError as e:
|
||||
pytest.fail(e)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("slug", ["project slug", "Project_Slug"])
|
||||
def test_invalid_slug(cookies, context, slug):
|
||||
"""Invalid slug should fail pre-generation hook."""
|
||||
context.update({"project_slug": slug})
|
||||
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code != 0
|
||||
assert isinstance(result.exception, FailedHookException)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("invalid_context", UNSUPPORTED_COMBINATIONS)
|
||||
def test_error_if_incompatible(cookies, context, invalid_context):
|
||||
"""It should not generate project an incompatible combination is selected."""
|
||||
context.update(invalid_context)
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code != 0
|
||||
assert isinstance(result.exception, FailedHookException)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["use_pycharm", "pycharm_docs_exist"],
|
||||
[
|
||||
("n", False),
|
||||
("y", True),
|
||||
],
|
||||
)
|
||||
def test_pycharm_docs_removed(cookies, context, use_pycharm, pycharm_docs_exist):
|
||||
context.update({"use_pycharm": use_pycharm})
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
with open(f"{result.project_path}/docs/index.rst") as f:
|
||||
has_pycharm_docs = "pycharm/configuration" in f.read()
|
||||
assert has_pycharm_docs is pycharm_docs_exist
|
||||
|
||||
|
||||
def test_trim_domain_email(cookies, context):
|
||||
"""Check that leading and trailing spaces are trimmed in domain and email."""
|
||||
context.update(
|
||||
{
|
||||
"use_docker": "y",
|
||||
"domain_name": " example.com ",
|
||||
"email": " me@example.com ",
|
||||
}
|
||||
)
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
prod_django_env = result.project_path / ".envs" / ".production" / ".django"
|
||||
assert "DJANGO_ALLOWED_HOSTS=.example.com" in prod_django_env.read_text()
|
||||
|
||||
base_settings = result.project_path / "config" / "settings" / "base.py"
|
||||
assert '"me@example.com"' in base_settings.read_text()
|
||||
|
|
|
@ -3,17 +3,20 @@
|
|||
# it is meant to be run from the root directory of the repository, eg:
|
||||
# sh tests/test_docker.sh
|
||||
|
||||
# install test requirements
|
||||
pip install -r requirements.txt
|
||||
set -o errexit
|
||||
set -x
|
||||
|
||||
# create a cache directory
|
||||
mkdir -p .cache/docker
|
||||
cd .cache/docker
|
||||
|
||||
# create the project using the default settings in cookiecutter.json
|
||||
cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y
|
||||
cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y "$@"
|
||||
cd my_awesome_project
|
||||
|
||||
# make sure all images build
|
||||
docker-compose -f local.yml build
|
||||
|
||||
# run the project's type checks
|
||||
docker-compose -f local.yml run django mypy my_awesome_project
|
||||
|
||||
|
@ -24,4 +27,16 @@ docker-compose -f local.yml run django pytest
|
|||
docker-compose -f local.yml run django python manage.py makemigrations --dry-run --check || { echo "ERROR: there were changes in the models, but migration listed above have not been created and are not saved in version control"; exit 1; }
|
||||
|
||||
# Test support for translations
|
||||
docker-compose -f local.yml run django python manage.py makemessages
|
||||
docker-compose -f local.yml run django python manage.py makemessages --all
|
||||
|
||||
# Make sure the check doesn't raise any warnings
|
||||
docker-compose -f local.yml run django python manage.py check --fail-level WARNING
|
||||
|
||||
# Generate the HTML for the documentation
|
||||
docker-compose -f local.yml run docs make html
|
||||
|
||||
# Run npm build script if package.json is present
|
||||
if [ -f "package.json" ]
|
||||
then
|
||||
docker-compose -f local.yml run node npm run build
|
||||
fi
|
||||
|
|
26
tests/test_hooks.py
Normal file
26
tests/test_hooks.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
"""Unit tests for the hooks"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from hooks.post_gen_project import append_to_gitignore_file
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def working_directory(tmp_path):
|
||||
prev_cwd = Path.cwd()
|
||||
os.chdir(tmp_path)
|
||||
try:
|
||||
yield tmp_path
|
||||
finally:
|
||||
os.chdir(prev_cwd)
|
||||
|
||||
|
||||
def test_append_to_gitignore_file(working_directory):
|
||||
gitignore_file = working_directory / ".gitignore"
|
||||
gitignore_file.write_text("node_modules/\n")
|
||||
append_to_gitignore_file(".envs/*")
|
||||
linesep = os.linesep.encode()
|
||||
assert gitignore_file.read_bytes() == b"node_modules/" + linesep + b".envs/*" + linesep
|
||||
assert gitignore_file.read_text() == "node_modules/\n.envs/*\n"
|
9
tox.ini
9
tox.ini
|
@ -1,7 +1,12 @@
|
|||
[tox]
|
||||
skipsdist = true
|
||||
envlist = py36
|
||||
envlist = py311,black-template
|
||||
|
||||
[testenv]
|
||||
deps = -rrequirements.txt
|
||||
commands = pytest {posargs:./tests}
|
||||
passenv = AUTOFIXABLE_STYLES
|
||||
commands = pytest -n auto {posargs:./tests}
|
||||
|
||||
[testenv:black-template]
|
||||
deps = black
|
||||
commands = black --check hooks tests setup.py docs scripts
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[run]
|
||||
include = {{cookiecutter.project_slug}}/*
|
||||
omit = *migrations*, *tests*
|
||||
plugins =
|
||||
django_coverage_plugin
|
|
@ -1,4 +1,11 @@
|
|||
.*
|
||||
!.coveragerc
|
||||
!.env
|
||||
!.pylintrc
|
||||
.editorconfig
|
||||
.gitattributes
|
||||
.github
|
||||
.gitignore
|
||||
.gitlab-ci.yml
|
||||
.idea
|
||||
.pre-commit-config.yaml
|
||||
.readthedocs.yml
|
||||
.travis.yml
|
||||
venv
|
||||
.git
|
||||
|
|
|
@ -12,13 +12,7 @@ trim_trailing_whitespace = true
|
|||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.py]
|
||||
line_length=120
|
||||
known_first_party={{ cookiecutter.project_slug }}
|
||||
multi_line_output=3
|
||||
default_section=THIRDPARTY
|
||||
|
||||
[*.{html,css,scss,json,yml}]
|
||||
[*.{html,css,scss,json,yml,xml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
@ -28,6 +22,6 @@ trim_trailing_whitespace = false
|
|||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[nginx.conf]
|
||||
[default.conf]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
USE_DOCKER=yes
|
||||
IPYTHONDIR=/app/.ipython
|
||||
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
# Redis
|
||||
# ------------------------------------------------------------------------------
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
{% if cookiecutter.use_celery == 'y' %}
|
||||
|
||||
# Celery
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Flower
|
||||
CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!!
|
||||
CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!!
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
# Caddy
|
||||
# ------------------------------------------------------------------------------
|
||||
DOMAIN_NAME={{ cookiecutter.domain_name }}
|
|
@ -13,24 +13,48 @@ DJANGO_SECURE_SSL_REDIRECT=False
|
|||
|
||||
# Email
|
||||
# ------------------------------------------------------------------------------
|
||||
MAILGUN_API_KEY=
|
||||
DJANGO_SERVER_EMAIL=
|
||||
{% if cookiecutter.mail_service == 'Mailgun' %}
|
||||
MAILGUN_API_KEY=
|
||||
MAILGUN_DOMAIN=
|
||||
|
||||
{% elif cookiecutter.mail_service == 'Mailjet' %}
|
||||
MAILJET_API_KEY=
|
||||
MAILJET_SECRET_KEY=
|
||||
{% elif cookiecutter.mail_service == 'Mandrill' %}
|
||||
MANDRILL_API_KEY=
|
||||
{% elif cookiecutter.mail_service == 'Postmark' %}
|
||||
POSTMARK_SERVER_TOKEN=
|
||||
{% elif cookiecutter.mail_service == 'Sendgrid' %}
|
||||
SENDGRID_API_KEY=
|
||||
SENDGRID_GENERATE_MESSAGE_ID=True
|
||||
SENDGRID_MERGE_FIELD_FORMAT=None
|
||||
{% elif cookiecutter.mail_service == 'SendinBlue' %}
|
||||
SENDINBLUE_API_KEY=
|
||||
{% elif cookiecutter.mail_service == 'SparkPost' %}
|
||||
SPARKPOST_API_KEY=
|
||||
{% endif %}
|
||||
{% if cookiecutter.cloud_provider == 'AWS' %}
|
||||
# AWS
|
||||
# ------------------------------------------------------------------------------
|
||||
DJANGO_AWS_ACCESS_KEY_ID=
|
||||
DJANGO_AWS_SECRET_ACCESS_KEY=
|
||||
DJANGO_AWS_STORAGE_BUCKET_NAME=
|
||||
|
||||
{% elif cookiecutter.cloud_provider == 'GCP' %}
|
||||
# GCP
|
||||
# ------------------------------------------------------------------------------
|
||||
GOOGLE_APPLICATION_CREDENTIALS=
|
||||
DJANGO_GCP_STORAGE_BUCKET_NAME=
|
||||
{% elif cookiecutter.cloud_provider == 'Azure' %}
|
||||
# Azure
|
||||
# ------------------------------------------------------------------------------
|
||||
DJANGO_AZURE_ACCOUNT_KEY=
|
||||
DJANGO_AZURE_ACCOUNT_NAME=
|
||||
DJANGO_AZURE_CONTAINER_NAME=
|
||||
{% endif %}
|
||||
# django-allauth
|
||||
# ------------------------------------------------------------------------------
|
||||
DJANGO_ACCOUNT_ALLOW_REGISTRATION=True
|
||||
{% if cookiecutter.use_compressor == 'y' %}
|
||||
# django-compressor
|
||||
# ------------------------------------------------------------------------------
|
||||
COMPRESS_ENABLED=
|
||||
{% endif %}
|
||||
|
||||
# Gunicorn
|
||||
# ------------------------------------------------------------------------------
|
||||
WEB_CONCURRENCY=4
|
||||
|
|
107
{{cookiecutter.project_slug}}/.github/dependabot.yml
vendored
Normal file
107
{{cookiecutter.project_slug}}/.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
# Config for Dependabot updates. See Documentation here:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# Update GitHub actions in workflows
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
|
||||
# Enable version updates for Docker
|
||||
# We need to specify each Dockerfile in a separate entry because Dependabot doesn't
|
||||
# support wildcards or recursively checking subdirectories. Check this issue for updates:
|
||||
# https://github.com/dependabot/dependabot-core/issues/2178
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/local/django` directory
|
||||
directory: 'compose/local/django/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
# Ignore minor version updates (3.10 -> 3.11) but update patch versions
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- 'version-update:semver-major'
|
||||
- 'version-update:semver-minor'
|
||||
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/local/docs` directory
|
||||
directory: 'compose/local/docs/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
# Ignore minor version updates (3.10 -> 3.11) but update patch versions
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- 'version-update:semver-major'
|
||||
- 'version-update:semver-minor'
|
||||
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/local/node` directory
|
||||
directory: 'compose/local/node/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/aws` directory
|
||||
directory: 'compose/production/aws/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/django` directory
|
||||
directory: 'compose/production/django/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
# Ignore minor version updates (3.10 -> 3.11) but update patch versions
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- 'version-update:semver-major'
|
||||
- 'version-update:semver-minor'
|
||||
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/postgres` directory
|
||||
directory: 'compose/production/postgres/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/traefik` directory
|
||||
directory: 'compose/production/traefik/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
{%- endif %}
|
||||
|
||||
# Enable version updates for Python/Pip - Production
|
||||
- package-ecosystem: 'pip'
|
||||
# Look for a `requirements.txt` in the `root` directory
|
||||
# also 'setup.cfg', 'runtime.txt' and 'requirements/*.txt'
|
||||
directory: '/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
|
||||
|
||||
# Enable version updates for javascript/npm
|
||||
- package-ecosystem: 'npm'
|
||||
# Look for a `packages.json` in the `root` directory
|
||||
directory: '/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
{%- endif %}
|
100
{{cookiecutter.project_slug}}/.github/workflows/ci.yml
vendored
Normal file
100
{{cookiecutter.project_slug}}/.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
name: CI
|
||||
|
||||
# Enable Buildkit and let compose use it to speed up image building
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
COMPOSE_DOCKER_CLI_BUILD: 1
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: ['master', 'main']
|
||||
paths-ignore: ['docs/**']
|
||||
|
||||
push:
|
||||
branches: ['master', 'main']
|
||||
paths-ignore: ['docs/**']
|
||||
|
||||
concurrency:
|
||||
group: {% raw %}${{ github.head_ref || github.run_id }}{% endraw %}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
linter:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
{%- if cookiecutter.open_source_license != 'Not open source' %}
|
||||
# Consider using pre-commit.ci for open source project
|
||||
{%- endif %}
|
||||
- name: Run pre-commit
|
||||
uses: pre-commit/action@v3.0.0
|
||||
|
||||
# With no caching at all the entire ci process takes 4m 30s to complete!
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
{%- if cookiecutter.use_docker == 'n' %}
|
||||
|
||||
services:
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
redis:
|
||||
image: redis:6
|
||||
ports:
|
||||
- 6379:6379
|
||||
{%- endif %}
|
||||
postgres:
|
||||
image: postgres:12
|
||||
ports:
|
||||
- 5432:5432
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
||||
env:
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
CELERY_BROKER_URL: 'redis://localhost:6379/0'
|
||||
{%- endif %}
|
||||
# postgres://user:password@host:port/database
|
||||
DATABASE_URL: 'postgres://postgres:postgres@localhost:5432/postgres'
|
||||
{%- endif %}
|
||||
|
||||
steps:
|
||||
- name: Checkout Code Repository
|
||||
uses: actions/checkout@v3
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
|
||||
- name: Build the Stack
|
||||
run: docker-compose -f local.yml build
|
||||
|
||||
- name: Run DB Migrations
|
||||
run: docker-compose -f local.yml run --rm django python manage.py migrate
|
||||
|
||||
- name: Run Django Tests
|
||||
run: docker-compose -f local.yml run django pytest
|
||||
|
||||
- name: Tear down the Stack
|
||||
run: docker-compose -f local.yml down
|
||||
{%- else %}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
requirements/base.txt
|
||||
requirements/local.txt
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements/local.txt
|
||||
|
||||
- name: Test with pytest
|
||||
run: pytest
|
||||
{%- endif %}
|
35
{{cookiecutter.project_slug}}/.gitignore
vendored
35
{{cookiecutter.project_slug}}/.gitignore
vendored
|
@ -159,6 +159,10 @@ typings/
|
|||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
|
||||
{% if cookiecutter.use_pycharm == 'y' -%}
|
||||
|
@ -321,29 +325,30 @@ Session.vim
|
|||
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
{% if cookiecutter.use_docker == 'n' %}
|
||||
|
||||
### VirtualEnv template
|
||||
# Virtualenv
|
||||
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
|
||||
[Bb]in
|
||||
[Ii]nclude
|
||||
[Ll]ib
|
||||
[Ll]ib64
|
||||
[Ss]cripts
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
.env
|
||||
{% endif %}
|
||||
# Redis dump file
|
||||
dump.rdb
|
||||
|
||||
### Project template
|
||||
{% if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' %}
|
||||
{%- if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' %}
|
||||
MailHog
|
||||
{%- endif %}
|
||||
{{ cookiecutter.project_slug }}/media/
|
||||
|
||||
.pytest_cache/
|
||||
|
||||
{% if cookiecutter.use_docker == 'y' %}
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
.ipython/
|
||||
{%- endif %}
|
||||
|
||||
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
|
||||
project.css
|
||||
project.min.css
|
||||
vendors.js
|
||||
*.min.js
|
||||
*.min.js.map
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
|
||||
{{ cookiecutter.project_slug }}/static/webpack_bundles/
|
||||
webpack-stats.json
|
||||
{%- endif %}
|
||||
|
|
54
{{cookiecutter.project_slug}}/.gitlab-ci.yml
Normal file
54
{{cookiecutter.project_slug}}/.gitlab-ci.yml
Normal file
|
@ -0,0 +1,54 @@
|
|||
stages:
|
||||
- lint
|
||||
- test
|
||||
|
||||
variables:
|
||||
POSTGRES_USER: '{{ cookiecutter.project_slug }}'
|
||||
POSTGRES_PASSWORD: ''
|
||||
POSTGRES_DB: 'test_{{ cookiecutter.project_slug }}'
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
CELERY_BROKER_URL: 'redis://redis:6379/0'
|
||||
{%- endif %}
|
||||
|
||||
precommit:
|
||||
stage: lint
|
||||
image: python:3.11
|
||||
variables:
|
||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
cache:
|
||||
paths:
|
||||
- ${PRE_COMMIT_HOME}
|
||||
before_script:
|
||||
- pip install -q pre-commit
|
||||
script:
|
||||
- pre-commit run --show-diff-on-failure --color=always --all-files
|
||||
|
||||
pytest:
|
||||
stage: test
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
image: docker/compose:1.29.2
|
||||
tags:
|
||||
- docker
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker-compose -f local.yml build
|
||||
# Ensure celerybeat does not crash due to non-existent tables
|
||||
- docker-compose -f local.yml run --rm django python manage.py migrate
|
||||
- docker-compose -f local.yml up -d
|
||||
script:
|
||||
- docker-compose -f local.yml run django pytest
|
||||
{%- else %}
|
||||
image: python:3.11
|
||||
tags:
|
||||
- python
|
||||
services:
|
||||
- postgres:{{ cookiecutter.postgresql_version }}
|
||||
variables:
|
||||
DATABASE_URL: pgsql://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB
|
||||
before_script:
|
||||
- pip install -r requirements/local.txt
|
||||
script:
|
||||
- pytest
|
||||
{%- endif %}
|
|
@ -0,0 +1,23 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration name="docker-compose up django" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker">
|
||||
<deployment type="docker-compose.yml">
|
||||
<settings>
|
||||
<option name="envFilePath" value=""/>
|
||||
<option name="services">
|
||||
<list>
|
||||
<option value="django"/>
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
<option value="celeryworker"/>
|
||||
<option value="celerybeat"/>
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
|
||||
<option value="node"/>
|
||||
{%- endif %}
|
||||
</list>
|
||||
</option>
|
||||
<option name="sourceFilePath" value="local.yml"/>
|
||||
</settings>
|
||||
</deployment>
|
||||
<method v="2"/>
|
||||
</configuration>
|
||||
</component>
|
|
@ -0,0 +1,16 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration name="docker-compose up docs" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker">
|
||||
<deployment type="docker-compose.yml">
|
||||
<settings>
|
||||
<option name="envFilePath" value=""/>
|
||||
<option name="services">
|
||||
<list>
|
||||
<option value="docs"/>
|
||||
</list>
|
||||
</option>
|
||||
<option name="sourceFilePath" value="local.yml"/>
|
||||
</settings>
|
||||
</deployment>
|
||||
<method v="2"/>
|
||||
</configuration>
|
||||
</component>
|
19
{{cookiecutter.project_slug}}/.idea/workspace.xml
Normal file
19
{{cookiecutter.project_slug}}/.idea/workspace.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
{%- if cookiecutter.use_docker == 'n' %}
|
||||
<component name="DjangoConsoleOptions"
|
||||
custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) import os sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)"
|
||||
module-name="{{ cookiecutter.project_slug }}" is-module-sdk="true">
|
||||
</component>
|
||||
{%- elif cookiecutter.use_celery == 'y' %}
|
||||
<component name="DjangoConsoleOptions"
|
||||
custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) import os os.environ.setdefault("DATABASE_URL","postgres://{}:{}@{}:{}/{}".format(os.environ['POSTGRES_USER'], os.environ['POSTGRES_PASSWORD'], os.environ['POSTGRES_HOST'], os.environ['POSTGRES_PORT'], os.environ['POSTGRES_DB'])) os.environ.setdefault("CELERY_BROKER_URL", os.environ['REDIS_URL']) sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)"
|
||||
module-name="{{ cookiecutter.project_slug }}" is-module-sdk="true">
|
||||
</component>
|
||||
{%- else %}
|
||||
<component name="DjangoConsoleOptions"
|
||||
custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) import os os.environ.setdefault("DATABASE_URL","postgres://{}:{}@{}:{}/{}".format(os.environ['POSTGRES_USER'], os.environ['POSTGRES_PASSWORD'], os.environ['POSTGRES_HOST'], os.environ['POSTGRES_PORT'], os.environ['POSTGRES_DB'])) sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)"
|
||||
module-name="{{ cookiecutter.project_slug }}" is-module-sdk="true">
|
||||
</component>
|
||||
{%- endif %}
|
||||
</project>
|
|
@ -13,7 +13,7 @@
|
|||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
{% if cookiecutter.js_task_runner != 'None' %}
|
||||
{% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/node_modules" />
|
||||
</content>
|
||||
|
@ -41,7 +41,7 @@
|
|||
</option>
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="projectConfiguration" value="py.test" />
|
||||
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
||||
<option name="projectConfiguration" value="pytest" />
|
||||
<option name="PROJECT_TEST_RUNNER" value="pytest" />
|
||||
</component>
|
||||
</module>
|
||||
|
|
66
{{cookiecutter.project_slug}}/.pre-commit-config.yaml
Normal file
66
{{cookiecutter.project_slug}}/.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,66 @@
|
|||
exclude: '^docs/|/migrations/'
|
||||
default_stages: [commit]
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: check-builtin-literals
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
- id: detect-private-key
|
||||
{%- if cookiecutter.frontend_pipeline in ["Webpack", "Gulp"] %}
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.0.0-alpha.9-for-vscode
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: ['--tab-width', '2', '--single-quote']
|
||||
exclude: '{{cookiecutter.project_slug}}/templates/'
|
||||
{%- endif %}
|
||||
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: '1.14.0'
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args: ['--target-version', '4.2']
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.7.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py311-plus]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
|
||||
- repo: https://github.com/Riverside-Healthcare/djLint
|
||||
rev: v1.31.1
|
||||
hooks:
|
||||
- id: djlint-reformat-django
|
||||
- id: djlint-django
|
||||
|
||||
# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
|
||||
ci:
|
||||
autoupdate_schedule: weekly
|
||||
skip: []
|
||||
submodules: false
|
|
@ -1,14 +0,0 @@
|
|||
[MASTER]
|
||||
load-plugins=pylint_common, pylint_django{% if cookiecutter.use_celery == "y" %}, pylint_celery {% endif %}
|
||||
|
||||
[FORMAT]
|
||||
max-line-length=120
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=missing-docstring,invalid-name
|
||||
|
||||
[DESIGN]
|
||||
max-parents=13
|
||||
|
||||
[TYPECHECK]
|
||||
generated-members=REQUEST,acl_users,aq_parent,"[a-zA-Z]+_set{1,2}",save,delete
|
20
{{cookiecutter.project_slug}}/.readthedocs.yml
Normal file
20
{{cookiecutter.project_slug}}/.readthedocs.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: '3.11'
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: requirements/local.txt
|
|
@ -1,11 +1,45 @@
|
|||
sudo: true
|
||||
before_install:
|
||||
dist: focal
|
||||
|
||||
language: python
|
||||
python:
|
||||
- "3.11"
|
||||
|
||||
services:
|
||||
- {% if cookiecutter.use_docker == 'y' %}docker{% else %}postgresql{% endif %}
|
||||
jobs:
|
||||
include:
|
||||
- name: "Linter"
|
||||
before_script:
|
||||
- pip install -q flake8
|
||||
script:
|
||||
- "flake8"
|
||||
|
||||
- name: "Django Test"
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
before_script:
|
||||
- docker-compose -v
|
||||
- docker -v
|
||||
- docker-compose -f local.yml build
|
||||
# Ensure celerybeat does not crash due to non-existent tables
|
||||
- docker-compose -f local.yml run --rm django python manage.py migrate
|
||||
- docker-compose -f local.yml up -d
|
||||
script:
|
||||
- "docker-compose -f local.yml run django pytest"
|
||||
after_failure:
|
||||
- docker-compose -f local.yml logs
|
||||
{%- else %}
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq build-essential gettext python-dev zlib1g-dev libpq-dev xvfb
|
||||
- sudo apt-get install -qq libtiff4-dev libjpeg8-dev libfreetype6-dev liblcms1-dev libwebp-dev
|
||||
- sudo apt-get install -qq libjpeg8-dev libfreetype6-dev libwebp-dev
|
||||
- sudo apt-get install -qq graphviz-dev python-setuptools python3-dev python-virtualenv python-pip
|
||||
- sudo apt-get install -qq firefox automake libtool libreadline6 libreadline6-dev libreadline-dev
|
||||
- sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
language: python
|
||||
python:
|
||||
- "3.11"
|
||||
install:
|
||||
- pip install -r requirements/local.txt
|
||||
script:
|
||||
- "pytest"
|
||||
{%- endif %}
|
||||
|
|
|
@ -7,7 +7,7 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
{% elif cookiecutter.open_source_license == 'BSD' %}
|
||||
{%- elif cookiecutter.open_source_license == 'BSD' %}
|
||||
Copyright (c) {% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}
|
||||
All rights reserved.
|
||||
|
||||
|
@ -35,7 +35,7 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{% elif cookiecutter.open_source_license == 'GPLv3' %}
|
||||
{%- elif cookiecutter.open_source_license == 'GPLv3' %}
|
||||
Copyright (c) {% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -50,4 +50,196 @@ GNU General Public License for more details.
|
|||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
{% endif %}
|
||||
{%- elif cookiecutter.open_source_license == 'Apache Software License 2.0' %}
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright {% now 'utc', '%Y' %} {{ cookiecutter.author_name }}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
release: python manage.py migrate
|
||||
{%- if cookiecutter.use_async == "y" %}
|
||||
web: gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker
|
||||
{%- else %}
|
||||
web: gunicorn config.wsgi:application
|
||||
{% if cookiecutter.use_celery == "y" -%}
|
||||
worker: celery worker --app={{cookiecutter.project_slug}}.taskapp --loglevel=info
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_celery == "y" %}
|
||||
worker: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app worker --loglevel=info
|
||||
beat: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app beat --loglevel=info
|
||||
{%- endif %}
|
||||
|
|
155
{{cookiecutter.project_slug}}/README.md
Normal file
155
{{cookiecutter.project_slug}}/README.md
Normal file
|
@ -0,0 +1,155 @@
|
|||
# {{cookiecutter.project_name}}
|
||||
|
||||
{{ cookiecutter.description }}
|
||||
|
||||
[](https://github.com/cookiecutter/cookiecutter-django/)
|
||||
[](https://github.com/ambv/black)
|
||||
|
||||
{%- if cookiecutter.open_source_license != "Not open source" %}
|
||||
|
||||
License: {{cookiecutter.open_source_license}}
|
||||
{%- endif %}
|
||||
|
||||
## Settings
|
||||
|
||||
Moved to [settings](http://cookiecutter-django.readthedocs.io/en/latest/settings.html).
|
||||
|
||||
## Basic Commands
|
||||
|
||||
### Setting Up Your Users
|
||||
|
||||
- To create a **normal user account**, just go to Sign Up and fill out the form. Once you submit it, you'll see a "Verify Your E-mail Address" page. Go to your console to see a simulated email verification message. Copy the link into your browser. Now the user's email should be verified and ready to go.
|
||||
|
||||
- To create a **superuser account**, use this command:
|
||||
|
||||
$ python manage.py createsuperuser
|
||||
|
||||
For convenience, you can keep your normal user logged in on Chrome and your superuser logged in on Firefox (or similar), so that you can see how the site behaves for both kinds of users.
|
||||
|
||||
### Type checks
|
||||
|
||||
Running type checks with mypy:
|
||||
|
||||
$ mypy {{cookiecutter.project_slug}}
|
||||
|
||||
### Test coverage
|
||||
|
||||
To run the tests, check your test coverage, and generate an HTML coverage report:
|
||||
|
||||
$ coverage run -m pytest
|
||||
$ coverage html
|
||||
$ open htmlcov/index.html
|
||||
|
||||
#### Running tests with pytest
|
||||
|
||||
$ pytest
|
||||
|
||||
### Live reloading and Sass CSS compilation
|
||||
|
||||
Moved to [Live reloading and SASS compilation](https://cookiecutter-django.readthedocs.io/en/latest/developing-locally.html#sass-compilation-live-reloading).
|
||||
|
||||
{%- if cookiecutter.use_celery == "y" %}
|
||||
|
||||
### Celery
|
||||
|
||||
This app comes with Celery.
|
||||
|
||||
To run a celery worker:
|
||||
|
||||
```bash
|
||||
cd {{cookiecutter.project_slug}}
|
||||
celery -A config.celery_app worker -l info
|
||||
```
|
||||
|
||||
Please note: For Celery's import magic to work, it is important _where_ the celery commands are run. If you are in the same folder with _manage.py_, you should be right.
|
||||
|
||||
To run [periodic tasks](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html), you'll need to start the celery beat scheduler service. You can start it as a standalone process:
|
||||
|
||||
```bash
|
||||
cd {{cookiecutter.project_slug}}
|
||||
celery -A config.celery_app beat
|
||||
```
|
||||
|
||||
or you can embed the beat service inside a worker with the `-B` option (not recommended for production use):
|
||||
|
||||
```bash
|
||||
cd {{cookiecutter.project_slug}}
|
||||
celery -A config.celery_app worker -B -l info
|
||||
```
|
||||
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_mailhog == "y" %}
|
||||
|
||||
### Email Server
|
||||
|
||||
{%- if cookiecutter.use_docker == "y" %}
|
||||
|
||||
In development, it is often nice to be able to see emails that are being sent from your application. For that reason local SMTP server [MailHog](https://github.com/mailhog/MailHog) with a web interface is available as docker container.
|
||||
|
||||
Container mailhog will start automatically when you will run all docker containers.
|
||||
Please check [cookiecutter-django Docker documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html) for more details how to start all containers.
|
||||
|
||||
With MailHog running, to view messages that are sent by your application, open your browser and go to `http://127.0.0.1:8025`
|
||||
{%- else %}
|
||||
|
||||
In development, it is often nice to be able to see emails that are being sent from your application. If you choose to use [MailHog](https://github.com/mailhog/MailHog) when generating the project a local SMTP server with a web interface will be available.
|
||||
|
||||
1. [Download the latest MailHog release](https://github.com/mailhog/MailHog/releases) for your OS.
|
||||
|
||||
2. Rename the build to `MailHog`.
|
||||
|
||||
3. Copy the file to the project root.
|
||||
|
||||
4. Make it executable:
|
||||
|
||||
$ chmod +x MailHog
|
||||
|
||||
5. Spin up another terminal window and start it there:
|
||||
|
||||
./MailHog
|
||||
|
||||
6. Check out <http://127.0.0.1:8025/> to see how it goes.
|
||||
|
||||
Now you have your own mail server running locally, ready to receive whatever you send it.
|
||||
|
||||
{%- endif %}
|
||||
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_sentry == "y" %}
|
||||
|
||||
### Sentry
|
||||
|
||||
Sentry is an error logging aggregator service. You can sign up for a free account at <https://sentry.io/signup/?code=cookiecutter> or download and host it yourself.
|
||||
The system is set up with reasonable defaults, including 404 logging and integration with the WSGI application.
|
||||
|
||||
You must set the DSN url in production.
|
||||
{%- endif %}
|
||||
|
||||
## Deployment
|
||||
|
||||
The following details how to deploy this application.
|
||||
{%- if cookiecutter.use_heroku.lower() == "y" %}
|
||||
|
||||
### Heroku
|
||||
|
||||
See detailed [cookiecutter-django Heroku documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-on-heroku.html).
|
||||
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_docker.lower() == "y" %}
|
||||
|
||||
### Docker
|
||||
|
||||
See detailed [cookiecutter-django Docker documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html).
|
||||
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
|
||||
|
||||
### Custom Bootstrap Compilation
|
||||
|
||||
The generated CSS is set up with automatic Bootstrap recompilation with variables of your choice.
|
||||
Bootstrap v5 is installed using npm and customised by tweaking your variables in `static/sass/custom_bootstrap_vars`.
|
||||
|
||||
You can find a list of available variables [in the bootstrap source](https://github.com/twbs/bootstrap/blob/v5.1.3/scss/_variables.scss), or get explanations on them in the [Bootstrap docs](https://getbootstrap.com/docs/5.1/customize/sass/).
|
||||
|
||||
Bootstrap's javascript as well as its dependencies are concatenated into a single file: `static/js/vendors.js`.
|
||||
{%- endif %}
|
|
@ -1,170 +0,0 @@
|
|||
{{cookiecutter.project_name}}
|
||||
{{ '=' * cookiecutter.project_name|length }}
|
||||
|
||||
{{cookiecutter.description}}
|
||||
|
||||
.. image:: https://img.shields.io/badge/built%20with-Cookiecutter%20Django-ff69b4.svg
|
||||
:target: https://github.com/pydanny/cookiecutter-django/
|
||||
:alt: Built with Cookiecutter Django
|
||||
{% if cookiecutter.open_source_license != "Not open source" %}
|
||||
|
||||
:License: {{cookiecutter.open_source_license}}
|
||||
{% endif %}
|
||||
|
||||
Settings
|
||||
--------
|
||||
|
||||
Moved to settings_.
|
||||
|
||||
.. _settings: http://cookiecutter-django.readthedocs.io/en/latest/settings.html
|
||||
|
||||
Basic Commands
|
||||
--------------
|
||||
|
||||
Setting Up Your Users
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* To create a **normal user account**, just go to Sign Up and fill out the form. Once you submit it, you'll see a "Verify Your E-mail Address" page. Go to your console to see a simulated email verification message. Copy the link into your browser. Now the user's email should be verified and ready to go.
|
||||
|
||||
* To create an **superuser account**, use this command::
|
||||
|
||||
$ python manage.py createsuperuser
|
||||
|
||||
For convenience, you can keep your normal user logged in on Chrome and your superuser logged in on Firefox (or similar), so that you can see how the site behaves for both kinds of users.
|
||||
|
||||
Type checks
|
||||
^^^^^^^^^^^
|
||||
|
||||
Running type checks with mypy:
|
||||
|
||||
::
|
||||
|
||||
$ mypy {{cookiecutter.project_slug}}
|
||||
|
||||
Test coverage
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
To run the tests, check your test coverage, and generate an HTML coverage report::
|
||||
|
||||
$ coverage run -m pytest
|
||||
$ coverage html
|
||||
$ open htmlcov/index.html
|
||||
|
||||
Running tests with py.test
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
$ pytest
|
||||
|
||||
Live reloading and Sass CSS compilation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Moved to `Live reloading and SASS compilation`_.
|
||||
|
||||
.. _`Live reloading and SASS compilation`: http://cookiecutter-django.readthedocs.io/en/latest/live-reloading-and-sass-compilation.html
|
||||
|
||||
{% if cookiecutter.use_celery == "y" %}
|
||||
|
||||
Celery
|
||||
^^^^^^
|
||||
|
||||
This app comes with Celery.
|
||||
|
||||
To run a celery worker:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd {{cookiecutter.project_slug}}
|
||||
celery -A {{cookiecutter.project_slug}}.taskapp worker -l info
|
||||
|
||||
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.
|
||||
|
||||
{% endif %}
|
||||
{% if cookiecutter.use_mailhog == "y" %}
|
||||
|
||||
Email Server
|
||||
^^^^^^^^^^^^
|
||||
{% if cookiecutter.use_docker == 'y' %}
|
||||
In development, it is often nice to be able to see emails that are being sent from your application. For that reason local SMTP server `MailHog`_ with a web interface is available as docker container.
|
||||
|
||||
Container mailhog will start automatically when you will run all docker containers.
|
||||
Please check `cookiecutter-django Docker documentation`_ for more details how to start all containers.
|
||||
|
||||
With MailHog running, to view messages that are sent by your application, open your browser and go to ``http://127.0.0.1:8025``
|
||||
{% else %}
|
||||
In development, it is often nice to be able to see emails that are being sent from your application. If you choose to use `MailHog`_ when generating the project a local SMTP server with a web interface will be available.
|
||||
|
||||
#. `Download the latest MailHog release`_ for your OS.
|
||||
|
||||
#. Rename the build to ``MailHog``.
|
||||
|
||||
#. Copy the file to the project root.
|
||||
|
||||
#. Make it executable: ::
|
||||
|
||||
$ chmod +x MailHog
|
||||
|
||||
#. Spin up another terminal window and start it there: ::
|
||||
|
||||
./MailHog
|
||||
|
||||
#. Check out `<http://127.0.0.1:8025/>`_ to see how it goes.
|
||||
|
||||
Now you have your own mail server running locally, ready to receive whatever you send it.
|
||||
|
||||
.. _`Download the latest MailHog release`: https://github.com/mailhog/MailHog/releases
|
||||
{% endif %}
|
||||
.. _mailhog: https://github.com/mailhog/MailHog
|
||||
{% endif %}
|
||||
{% if cookiecutter.use_sentry == "y" %}
|
||||
|
||||
Sentry
|
||||
^^^^^^
|
||||
|
||||
Sentry is an error logging aggregator service. You can sign up for a free account at https://sentry.io/signup/?code=cookiecutter or download and host it yourself.
|
||||
The system is setup with reasonable defaults, including 404 logging and integration with the WSGI application.
|
||||
|
||||
You must set the DSN url in production.
|
||||
{% endif %}
|
||||
|
||||
Deployment
|
||||
----------
|
||||
|
||||
The following details how to deploy this application.
|
||||
{% if cookiecutter.use_heroku.lower() == "y" %}
|
||||
|
||||
Heroku
|
||||
^^^^^^
|
||||
|
||||
See detailed `cookiecutter-django Heroku documentation`_.
|
||||
|
||||
.. _`cookiecutter-django Heroku documentation`: http://cookiecutter-django.readthedocs.io/en/latest/deployment-on-heroku.html
|
||||
{% endif %}
|
||||
{% if cookiecutter.use_docker.lower() == "y" %}
|
||||
|
||||
Docker
|
||||
^^^^^^
|
||||
|
||||
See detailed `cookiecutter-django Docker documentation`_.
|
||||
|
||||
.. _`cookiecutter-django Docker documentation`: http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html
|
||||
{% endif %}
|
||||
|
||||
{% if cookiecutter.custom_bootstrap_compilation == "y" %}
|
||||
Custom Bootstrap Compilation
|
||||
^^^^^^
|
||||
|
||||
The generated CSS is set up with automatic Bootstrap recompilation with variables of your choice.
|
||||
Bootstrap v4.1.1 is installed using npm and customised by tweaking your variables in ``static/sass/custom_bootstrap_vars``.
|
||||
|
||||
You can find a list of available variables `in the bootstrap source`_, or get explanations on them in the `Bootstrap docs`_.
|
||||
|
||||
{% if cookiecutter.js_task_runner == 'Gulp' %}
|
||||
Bootstrap's javascript as well as its dependencies is concatenated into a single file: ``static/js/vendors.js``.
|
||||
{% endif %}
|
||||
|
||||
.. _in the bootstrap source: https://github.com/twbs/bootstrap/blob/v4-dev/scss/_variables.scss
|
||||
.. _Bootstrap docs: https://getbootstrap.com/docs/4.1/getting-started/theming/
|
||||
|
||||
{% endif %}
|
26
{{cookiecutter.project_slug}}/bin/post_compile
Normal file
26
{{cookiecutter.project_slug}}/bin/post_compile
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env bash
|
||||
{%- if cookiecutter.frontend_pipeline == "Django Compressor" %}
|
||||
|
||||
compress_enabled() {
|
||||
python << END
|
||||
import sys
|
||||
|
||||
from environ import Env
|
||||
|
||||
env = Env(COMPRESS_ENABLED=(bool, True))
|
||||
if env('COMPRESS_ENABLED'):
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
END
|
||||
}
|
||||
|
||||
if compress_enabled
|
||||
then
|
||||
python manage.py compress
|
||||
fi
|
||||
{%- endif %}
|
||||
|
||||
python manage.py collectstatic --noinput
|
||||
python manage.py compilemessages -i site-packages
|
|
@ -1,44 +1,79 @@
|
|||
FROM python:3.6-alpine
|
||||
# define an alias for the specific python version used in this file.
|
||||
FROM python:3.11.4-slim-bullseye as python
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
# Python build stage
|
||||
FROM python as python-build-stage
|
||||
|
||||
RUN apk update \
|
||||
ARG BUILD_ENVIRONMENT=local
|
||||
|
||||
# Install apt packages
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
# dependencies for building Python packages
|
||||
build-essential \
|
||||
# psycopg2 dependencies
|
||||
&& apk add --virtual build-deps gcc python3-dev musl-dev \
|
||||
&& apk add postgresql-dev \
|
||||
# Pillow dependencies
|
||||
&& apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \
|
||||
# CFFI dependencies
|
||||
&& apk add libffi-dev py-cffi \
|
||||
# Translations dependencies
|
||||
&& apk add gettext \
|
||||
# https://docs.djangoproject.com/en/dev/ref/django-admin/#dbshell
|
||||
&& apk add postgresql-client
|
||||
libpq-dev
|
||||
|
||||
# Requirements are installed here to ensure they will be cached.
|
||||
COPY ./requirements /requirements
|
||||
RUN pip install -r /requirements/local.txt
|
||||
COPY ./requirements .
|
||||
|
||||
# Create Python Dependency and Sub-Dependency Wheels.
|
||||
RUN pip wheel --wheel-dir /usr/src/app/wheels \
|
||||
-r ${BUILD_ENVIRONMENT}.txt
|
||||
|
||||
|
||||
# Python 'run' stage
|
||||
FROM python as python-run-stage
|
||||
|
||||
ARG BUILD_ENVIRONMENT=local
|
||||
ARG APP_HOME=/app
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV BUILD_ENV ${BUILD_ENVIRONMENT}
|
||||
|
||||
WORKDIR ${APP_HOME}
|
||||
|
||||
# Install required system dependencies
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
# psycopg2 dependencies
|
||||
libpq-dev \
|
||||
# Translations dependencies
|
||||
gettext \
|
||||
# cleaning up unused files
|
||||
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction
|
||||
# copy python dependency wheels from python-build-stage
|
||||
COPY --from=python-build-stage /usr/src/app/wheels /wheels/
|
||||
|
||||
# use wheels to install python dependencies
|
||||
RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \
|
||||
&& rm -rf /wheels/
|
||||
|
||||
COPY ./compose/production/django/entrypoint /entrypoint
|
||||
RUN sed -i 's/\r//' /entrypoint
|
||||
RUN sed -i 's/\r$//g' /entrypoint
|
||||
RUN chmod +x /entrypoint
|
||||
|
||||
COPY ./compose/local/django/start /start
|
||||
RUN sed -i 's/\r//' /start
|
||||
RUN sed -i 's/\r$//g' /start
|
||||
RUN chmod +x /start
|
||||
|
||||
{% if cookiecutter.use_celery == "y" %}
|
||||
COPY ./compose/local/django/celery/worker/start /start-celeryworker
|
||||
RUN sed -i 's/\r//' /start-celeryworker
|
||||
RUN sed -i 's/\r$//g' /start-celeryworker
|
||||
RUN chmod +x /start-celeryworker
|
||||
|
||||
COPY ./compose/local/django/celery/beat/start /start-celerybeat
|
||||
RUN sed -i 's/\r//' /start-celerybeat
|
||||
RUN sed -i 's/\r$//g' /start-celerybeat
|
||||
RUN chmod +x /start-celerybeat
|
||||
|
||||
COPY ./compose/local/django/celery/flower/start /start-flower
|
||||
RUN sed -i 's/\r//' /start-flower
|
||||
RUN sed -i 's/\r$//g' /start-flower
|
||||
RUN chmod +x /start-flower
|
||||
{% endif %}
|
||||
WORKDIR /app
|
||||
|
||||
# copy application code to WORKDIR
|
||||
COPY . ${APP_HOME}
|
||||
|
||||
ENTRYPOINT ["/entrypoint"]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
|
||||
rm -f './celerybeat.pid'
|
||||
celery -A {{cookiecutter.project_slug}}.taskapp beat -l INFO
|
||||
exec watchfiles --filter python celery.__main__.main --args '-A config.celery_app beat -l INFO'
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
|
||||
celery flower \
|
||||
--app={{cookiecutter.project_slug}}.taskapp \
|
||||
--broker="${CELERY_BROKER_URL}" \
|
||||
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
|
||||
exec watchfiles --filter python celery.__main__.main \
|
||||
--args \
|
||||
"-A config.celery_app -b \"${CELERY_BROKER_URL}\" flower --basic_auth=\"${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}\""
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
|
||||
celery -A {{cookiecutter.project_slug}}.taskapp worker -l INFO
|
||||
exec watchfiles --filter python celery.__main__.main --args '-A config.celery_app worker -l INFO'
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user