Skip to content

Commit

Permalink
Merge pull request #13 from veselosky/self-hosted
Browse files Browse the repository at this point in the history
Update to latest commoncontent and Dockerize for self-hosting
  • Loading branch information
veselosky authored Dec 30, 2024
2 parents edb062f + 700d459 commit 3eba7f4
Show file tree
Hide file tree
Showing 52 changed files with 3,075 additions and 638 deletions.
27 changes: 27 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.tox
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ jobs:
uses: ./.github/workflows/test.yml

create_release:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: run_tests
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Check out the code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Create a Github Release
run: gh release create ${{ github.ref }} --generate-notes
11 changes: 5 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ on:

jobs:
run_tests:
if: ${{ github.repository != 'veselosky/django-project-template' }}
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.12", "3.13"]
steps:
- name: Check out the code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
Expand All @@ -27,4 +26,4 @@ jobs:
pip install build tox tox-gh-actions
- name: Run the test suite
run: tox
run: tox run
75 changes: 75 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
exclude: '^docs/|/migrations/|devcontainer.json'
default_stages: [pre-commit]

default_language_version:
python: python3.12

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: check-case-conflict
- id: check-docstring-first
- id: check-merge-conflict
- id: check-symlinks
- id: destroyed-symlinks
- id: detect-private-key
- id: no-commit-to-branch

- repo: https://github.com/adamchainz/django-upgrade
rev: '1.20.0'
hooks:
- id: django-upgrade
args: ['--target-version', '4.2']

# Run the Ruff linter.
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.3
hooks:
# Linter
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
# Formatter
- id: ruff-format

- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.34.1
hooks:
- id: djlint-reformat-django
- id: djlint-django

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.19
hooks:
- id: validate-pyproject
# Optional extra validations from SchemaStore:
additional_dependencies: [
"validate-pyproject-schema-store[all]",
"tomli",
"packaging",
"trove-classifiers",
]

- repo: local
hooks:
- id: check-migrations
name: Check for ungenerated migrations
language: system
pass_filenames: false
entry: .venv/bin/python manage.py makemigrations --no-input --dry-run --check
- id: validate-templates
name: Validate Django template syntax
language: system
pass_filenames: false
entry: .venv/bin/python manage.py validate_templates

# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
ci:
autoupdate_schedule: weekly
skip: []
submodules: false
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.10
6 changes: 5 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"recommendations": ["ms-python.isort", "ms-python.python", "batisteo.vscode-django"]
"recommendations": [
"ms-python.python",
"batisteo.vscode-django",
"charliermarsh.ruff"
]
}
17 changes: 16 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,27 @@
"configurations": [
{
"name": "Runserver Django",
"type": "python",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": ["runserver"],
"django": true,
"justMyCode": true
},
{
"name": "Docker: Python - Django",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"python": {
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
],
"projectType": "django"
}
}
]
}
9 changes: 3 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
"source.organizeImports": "always"
},
"editor.defaultFormatter": "ms-python.black-formatter"
"editor.defaultFormatter": "charliermarsh.ruff"
},
"isort.importStrategy": "fromEnvironment",
"isort.args": ["--profile", "black"],
"python.analysis.autoImportCompletions": true,
"python.formatting.provider": "none"
"python.analysis.autoImportCompletions": true
}
20 changes: 20 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"type": "docker-build",
"label": "docker-build",
"platform": "python",
"dockerBuild": {
"tag": "storyville:latest",
"dockerfile": "${workspaceFolder}/Dockerfile",
"context": "${workspaceFolder}",
"pull": true
}
},
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"python": {
"args": ["runserver", "0.0.0.0:8000", "--nothreading", "--noreload"],
"file": "manage.py"
}
}
]
}
29 changes: 29 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# An example Caddyfile for serving the Django site (locally)

# Specifying port 80 (or using the http:// prefix) tells Caddy not to bother with SSL
# certs from Let's Encrypt. This is better for local development. In production, use
# the actual domain name without a port number to get automatic SSL certs.
localhost:80 {
root * /var/www # Set working dir to /var/www for this site

# Enable logging. Access logs go to stderr by default. This is what you want for
# contanerized applications, as logs are automatically collected by Docker/k8s.
log

# Creates a matcher that matches any file that exists in the root.
@static file
# Serve files from the root directory if the request matches the @static matcher
# (that is, if they exist).
# Handle blocks are mutually exclusive, so the first one to match will be used, and
# the rest will be ignored.
handle @static {
file_server
}

# The handle block without a matcher is the fallback. It will match any request that
# is not handled by previous handle blocks. Here, we reverse proxy to the Django app
# by default.
handle {
reverse_proxy http://storyville:8000
}
}
53 changes: 53 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# First, build the application in the `/app` directory using uv.
ARG PYTHON_VERSION=3.12
FROM ghcr.io/astral-sh/uv:python${PYTHON_VERSION}-bookworm-slim AS builder
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
WORKDIR /app
# For good layer caching, create the virtualenv before copying the source code
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project --no-dev
# Production image doesn't need tests or other stuff at top level (but it DOES
# need the REAMDE.md because it's referenced in pyproject.toml)
COPY ./README.md /app/
COPY ./src/ /app/

# Install the app itself
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-dev

# TODO If using i18n, compile the translations with the compilemessages command

################################################################################
# Then, use a final image without uv
FROM python:${PYTHON_VERSION}-slim-bookworm
# It is important to use the image that matches the builder, as the path to the
# Python executable must be the same, e.g., using `python:3.11-slim-bookworm`
# will fail.

RUN useradd -r -U app
# Declare volumes that need to be mounted from the host. `db` holds the SQLite
# database, and `www` holds static files and uploaded media to be served by the
# web server.
VOLUME ["/app/var/db", "/app/var/www"]

# Copy the application from the builder
COPY --from=builder --chown=app:app /app /app

# Place executables in the environment at the front of the path
ENV PATH="/app/.venv/bin:$PATH"
# set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 DJANGO_SETTINGS_MODULE=storyville.settings

# Switch to the app user
USER app

# Add metadata
LABEL org.opencontainers.image.source=https://github.com/veselosky/storyville

# Use Granian to run the application
EXPOSE 8000
CMD ["granian", "--interface", "wsgi", "--workers", "4", "--host", "0.0.0.0", "storyville.wsgi:application"]
41 changes: 23 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
# Storyville: Vince's little corner of the web

This Django project houses my actively maintained websites, and custom web-based tools I
use.
This Django project houses my actively maintained websites.

## Dependency Management
## Environment: Local vs Docker

This project includes [pip-tools](https://pypi.org/project/pip-tools/) for dependency
management. There are two requirements files: `requirements.in` provides the acceptable
ranges of packages to install in a production environment (or any other environment);
`requirements-dev.in` provides packages to install in development environments. Both of
these have corresponding "pin" files: `requirements.txt` and `requirements-dev.txt`.
Default values in the project's `settings.py` are suitable for running inside a Docker
container. For developing locally (running the app on local host and not in a Docker
container) copy the `example.env` file to `.env` within the project base directory and
adjust variables accordingly.

To add a new dependency, add it to the correct `.in` file, and then run
`manage.py pipsync` to regenerate the pin files and synchronize your current virtual
environment with the new pin files.
To simplify both development and production, data files that need backing up are stored
together under a single directory, designated by the `DATA_DIR` environment variable.
The SQLite database files are stored under `$DATA_DIR/db/`. STATIC_ROOT and MEDIA_ROOT
are subdirectories stored under `$DATA_DIR/www`.

Any arguments passed to `manage.py pipsync` will be passed through to the underlying
`pip-compile` command. For example, to bump to the latest Django patch release use
`manage.py pipsync --upgrade-package django`. See the
[pip-tools docs](https://pypi.org/project/pip-tools/) for complete details.
In the Docker container, `DATA_DIR` has the default value of `/app/var/`. To run under
Docker, provide two volumes or bind mounts, one for `/app/var/db/`, and a second for
`/app/var/www/`. The www volume/mount must be shared with your web server to serve
static files and media.

The pin files are not included in the template repository, but will be generated when
you run `manage.py devsetup`. This ensures you will get the latest version of Django and
related packages when starting a new project.
When running in a Docker container, several environment variables MUST be provided:

- SECRET_KEY - App will not run without it.
- ALLOWED_HOSTS - For dev, set to "\*". For production, the sites you will serve.
- SITE_ID - Not required for production (assuming DNS is correct), but needed for local
dev.
- CACHE_URL - Using the example compose file, set to `redis://redis:6379/0`

TODO: Automate building container image on release
Loading

0 comments on commit 3eba7f4

Please sign in to comment.