aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.dockerignore5
-rw-r--r--.flake82
-rw-r--r--.gitignore2
-rw-r--r--Pipfile5
-rw-r--r--Pipfile.lock68
-rw-r--r--docker-compose.yml5
-rw-r--r--docker/Dockerfile (renamed from docker/app/Dockerfile)11
-rw-r--r--docker/app/build-wiki.Dockerfile2
-rw-r--r--docker/app/local.Dockerfile28
-rw-r--r--docker/app/scripts/build-wiki.sh4
-rwxr-xr-xdocker/app/scripts/migrate.sh10
-rwxr-xr-xdocker/app/scripts/migrate_and_serve.sh21
-rw-r--r--docker/uwsgi.ini (renamed from docker/app/uwsgi.ini)0
-rw-r--r--docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl (renamed from docker/app/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl)bin1287002 -> 1287002 bytes
-rwxr-xr-xmanage.py140
15 files changed, 197 insertions, 106 deletions
diff --git a/.dockerignore b/.dockerignore
index 771913a3..236295ca 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -17,9 +17,8 @@ pydis_site/apps/staff/tests
CHANGELOG.md
CONTRIBUTING.md
docker
-!docker/app/scripts/migrate.sh
-!docker/app/uwsgi.ini
-!docker/app/wheels
+!docker/uwsgi.ini
+!docker/wheels
docker-compose.yml
Dockerfile.local
docs
diff --git a/.flake8 b/.flake8
index e36c2eb1..699d9650 100644
--- a/.flake8
+++ b/.flake8
@@ -3,7 +3,7 @@ max-line-length=100
docstring-convention=all
import-order-style=pycharm
application_import_names=pydis_site
-exclude=__pycache__, venv, .venv, manage.py, **/migrations
+exclude=__pycache__, venv, .venv, **/migrations
ignore=
B311,W503,E226,S311,T000
# Missing Docstrings
diff --git a/.gitignore b/.gitignore
index 24bdf199..b47aa2c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,7 +117,7 @@ media/
pip-wheel-metadata/
staticfiles/
-!docker/app/wheels
+!docker/wheels
*.js.tmp
log.*
diff --git a/Pipfile b/Pipfile
index af5165df..a236622d 100644
--- a/Pipfile
+++ b/Pipfile
@@ -18,8 +18,9 @@ whitenoise = "==4.1.2"
requests = "~=2.21"
pygments = "~=2.3.1"
#wiki = {git = "https://github.com/python-discord/django-wiki.git"}
-wiki = {path = "./docker/app/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl"}
-pyyaml = "*"
+wiki = {path = "./docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl"}
+pyyaml = "~=5.1"
+pyuwsgi = "~=2.0"
[dev-packages]
coverage = "~=4.5.3"
diff --git a/Pipfile.lock b/Pipfile.lock
index 01345754..5339ad58 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "d0e378fcea422eb28537a91e60e7faab8ae08a4ad9e3f1bd71fe89d377d36a8d"
+ "sha256": "ec1cd3daf99e1b368137579c284c79b3dfe5c6eb151758aee102045fddcd09e4"
},
"pipfile-spec": 6,
"requires": {
@@ -262,6 +262,36 @@
],
"version": "==2019.2"
},
+ "pyuwsgi": {
+ "hashes": [
+ "sha256:15a4626740753b0d0dfeeac7d367f9b2e89ab6af16c195927e60f75359fc1bbc",
+ "sha256:24c40c3b889eb9f283d43feffbc0f7c7fc024e914451425156ddb68af3df1e71",
+ "sha256:393737bd43a7e38f0a4a1601a37a69c4bf893635b37665ff958170fdb604fdb7",
+ "sha256:5a08308f87e639573c1efaa5966a6d04410cd45a73c4586a932fe3ee4b56369d",
+ "sha256:5f4b36c0dbb9931c4da8008aa423158be596e3b4a23cec95a958631603a94e45",
+ "sha256:7c31794f71bbd0ccf542cab6bddf38aa69e84e31ae0f9657a2e18ebdc150c01a",
+ "sha256:802ec6dad4b6707b934370926ec1866603abe31ba03c472f56149001b3533ba1",
+ "sha256:814d73d4569add69a6c19bb4a27cd5adb72b196e5e080caed17dbda740402072",
+ "sha256:829299cd117cf8abe837796bf587e61ce6bfe18423a3a1c510c21e9825789c2c",
+ "sha256:85f2210ceae5f48b7d8fad2240d831f4b890cac85cd98ca82683ac6aa481dfc8",
+ "sha256:861c94442b28cd64af033e88e0f63c66dbd5609f67952dc18694098b47a43f3a",
+ "sha256:957bc6316ffc8463795d56d9953d58e7f32aa5aad1c5ac80bc45c69f3299961e",
+ "sha256:9760c3f56fb5f15852d163429096600906478e9ed2c189a52f2bb21d8a2a986c",
+ "sha256:a4b24703ea818196d0be1dc64b3b57b79c67e8dee0cfa207a4216220912035a7",
+ "sha256:ad7f4968c1ddbf139a306d9b075360d959cc554d994ba5e1f512af9a40e62357",
+ "sha256:b1127d34b90f74faf1707718c57a4193ac028b9f4aec0238638983132297d456",
+ "sha256:bcb04d6ec644b3e08d03c64851e06edd7110489261e50627a4bcadf66ff6920e",
+ "sha256:bebfebb9ee83d7cf37668bf54275b677b7ae283e84a944f9f3ac6a4b66f95d4b",
+ "sha256:c29892dafc65a8b6eb95823fa4bac7754ca3fd1c28ab8d2a973289531b340a27",
+ "sha256:cb296b50b51ba022b0090b28d032ff1dd395a6db03672b65a39e83532edad527",
+ "sha256:ce777ebdf49ce736fc04abf555b5c41ab3f130127543a689dcf8d4871cd18fe4",
+ "sha256:d8b4bf930b6a19bc9ee982b9163d948c87501ad91b71516924e8ed25fe85d2ee",
+ "sha256:e2a420f2c4d35f3ec0b7e752a80d7bd385e2c5a64f67c05f2d2d74230e3114b6",
+ "sha256:fed899ce96f4f2b4d1b9f338dd145a4040ee1d8a5152213af0dd8d4a4d36e9fe"
+ ],
+ "index": "pypi",
+ "version": "==2.0.18.post0"
+ },
"pyyaml": {
"hashes": [
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
@@ -336,7 +366,7 @@
"hashes": [
"sha256:73a53bc770ce6b1d2ea6916d81ccefe40751d87b30556fa3b992c85b7fde8534"
],
- "path": "./docker/app/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl",
+ "path": "./docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl",
"version": "==0.5.dev20190420204942"
}
},
@@ -369,14 +399,6 @@
],
"version": "==2.0.1"
},
- "colorama": {
- "hashes": [
- "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d",
- "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"
- ],
- "markers": "platform_system == 'Windows'",
- "version": "==0.4.1"
- },
"coverage": {
"hashes": [
"sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6",
@@ -432,11 +454,11 @@
},
"flake8-annotations": {
"hashes": [
- "sha256:1309f2bc9853a2d77d578b089d331b0b832b40c97932641e136e1b49d3650c82",
- "sha256:3ecdd27054c3eed6484139025698465e3c9f4e68dbd5043d0204fcb2550ee27b"
+ "sha256:6ac7ca1e706307686b60af8043ff1db31dc2cfc1233c8210d67a3d9b8f364736",
+ "sha256:b51131007000d67217608fa028a35ff80aa400b474e5972f1f99c2cf9d26bd2e"
],
"index": "pypi",
- "version": "==1.0.0"
+ "version": "==1.1.0"
},
"flake8-bandit": {
"hashes": [
@@ -646,6 +668,26 @@
],
"version": "==0.10.0"
},
+ "typed-ast": {
+ "hashes": [
+ "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
+ "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
+ "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
+ "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
+ "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
+ "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
+ "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
+ "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
+ "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
+ "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
+ "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
+ "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
+ "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
+ "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
+ "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
+ ],
+ "version": "==1.4.0"
+ },
"unittest-xml-reporting": {
"hashes": [
"sha256:140982e4b58e4052d9ecb775525b246a96bfc1fc26097806e05ea06e9166dd6c",
diff --git a/docker-compose.yml b/docker-compose.yml
index 4ea110c4..71a5593f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -22,8 +22,8 @@ services:
web:
build:
context: .
- dockerfile: docker/app/local.Dockerfile
- command: docker/app/scripts/migrate_and_serve.sh
+ dockerfile: docker/Dockerfile
+ command: ["run", "--debug"]
ports:
- "127.0.0.1:8000:8000"
depends_on:
@@ -33,7 +33,6 @@ services:
- staticfiles:/var/www/static
environment:
DATABASE_URL: postgres://pysite:pysite@postgres:5432/pysite
- DEBUG: "true"
SECRET_KEY: suitable-for-development-only
STATIC_ROOT: /var/www/static
diff --git a/docker/app/Dockerfile b/docker/Dockerfile
index a6986fb2..aa427947 100644
--- a/docker/app/Dockerfile
+++ b/docker/Dockerfile
@@ -11,8 +11,8 @@ ENV PIP_NO_CACHE_DIR=false \
# Create non-root user.
RUN useradd --system --shell /bin/false --uid 1500 pysite
-# Install pipenv & pyuwsgi
-RUN pip install -U pipenv pyuwsgi
+# Install pipenv
+RUN pip install -U pipenv
# Copy the project files into working directory
WORKDIR /app
@@ -21,7 +21,6 @@ COPY . .
# Install project dependencies
RUN pipenv install --system --deploy
-# Migrate, collect and start the app
-RUN chmod +x /app/docker/app/scripts/migrate.sh
-ENTRYPOINT ["/app/docker/app/scripts/migrate.sh"]
-CMD ["uwsgi", "--ini", "docker/app/uwsgi.ini"]
+# Run web server through custom manager
+ENTRYPOINT ["python", "manage.py"]
+CMD ["run"]
diff --git a/docker/app/build-wiki.Dockerfile b/docker/app/build-wiki.Dockerfile
deleted file mode 100644
index 92003377..00000000
--- a/docker/app/build-wiki.Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM python:3.7
-RUN pip --no-cache-dir wheel --wheel-dir=/wheels "wiki @ git+https://github.com/python-discord/django-wiki.git"
diff --git a/docker/app/local.Dockerfile b/docker/app/local.Dockerfile
deleted file mode 100644
index 9e15c438..00000000
--- a/docker/app/local.Dockerfile
+++ /dev/null
@@ -1,28 +0,0 @@
-FROM python:3.7-slim
-
-# Allow service to handle stops gracefully
-STOPSIGNAL SIGQUIT
-
-# Set pip to have cleaner logs and no saved cache
-ENV PIP_NO_CACHE_DIR=false \
- PIPENV_HIDE_EMOJIS=1 \
- PIPENV_NOSPIN=1
-
-# Create non-root user
-RUN useradd --system --shell /bin/false --uid 1500 pysite
-
-# Install pipenv & pyuwsgi
-RUN pip install -U pipenv pyuwsgi
-
-# Copy the project files into working directory
-WORKDIR /app
-COPY . .
-
-# Install project dependencies
-RUN pipenv install --system --deploy
-
-# Prepare static files for site
-RUN SECRET_KEY=placeholder DATABASE_URL=sqlite:// \
- python3 manage.py collectstatic --no-input --clear --verbosity 0
-
-CMD ["uwsgi", "--ini", "docker/app/uwsgi.ini"]
diff --git a/docker/app/scripts/build-wiki.sh b/docker/app/scripts/build-wiki.sh
deleted file mode 100644
index 07c54f66..00000000
--- a/docker/app/scripts/build-wiki.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-docker build -t build_uwsgi -f docker/app/build-wiki.Dockerfile .
-CONTAINER=$(docker run -itd build_uwsgi /bin/bash)
-docker cp "$CONTAINER:/wheels" docker/app
-docker stop "$CONTAINER"
diff --git a/docker/app/scripts/migrate.sh b/docker/app/scripts/migrate.sh
deleted file mode 100755
index 22636c93..00000000
--- a/docker/app/scripts/migrate.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-
-echo --- Applying migrations ---
-python manage.py migrate --verbosity 1
-
-echo --- Collecting static files ---
-python manage.py collectstatic --no-input --clear --verbosity 1
-
-echo --- Starting uwsgi ---
-exec "$@" # This runs the CMD at the end of the Dockerfile
diff --git a/docker/app/scripts/migrate_and_serve.sh b/docker/app/scripts/migrate_and_serve.sh
deleted file mode 100755
index c30d7e04..00000000
--- a/docker/app/scripts/migrate_and_serve.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-set -eu
-
-### NOTE
-# This file is intended to be used by local setups.
-# You do not want to run the Django development server
-# in production. The default Dockerfile command will
-# run using uWSGI, this script is provided purely as
-# a convenience to run migrations and start a development server.
-
-echo [i] Applying migrations.
-python manage.py migrate --verbosity 1
-
-echo [i] Collecting static files.
-python manage.py collectstatic --no-input --clear --verbosity 1
-
-echo [i] Creating a superuser.
-echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin', 'admin') if not User.objects.filter(username='admin').exists() else print('Admin user already exists')" | python manage.py shell
-
-echo [i] Starting server.
-python manage.py runserver 0.0.0.0:8000
diff --git a/docker/app/uwsgi.ini b/docker/uwsgi.ini
index 3f35258c..3f35258c 100644
--- a/docker/app/uwsgi.ini
+++ b/docker/uwsgi.ini
diff --git a/docker/app/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl b/docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl
index b7637e76..b7637e76 100644
--- a/docker/app/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl
+++ b/docker/wheels/wiki-0.5.dev20190420204942-py3-none-any.whl
Binary files differ
diff --git a/manage.py b/manage.py
index b257ea65..8838b810 100755
--- a/manage.py
+++ b/manage.py
@@ -1,21 +1,137 @@
#!/usr/bin/env python
import os
+import re
+import socket
import sys
+import time
+from typing import List
+import django
+import pyuwsgi
+from django.contrib.auth import get_user_model
+from django.core.management import call_command, execute_from_command_line
-# Separate definition to ease calling this in other scripts.
-def main():
+
+DEFAULT_ENVS = {
+ "DJANGO_SETTINGS_MODULE": "pydis_site.settings",
+ "SUPER_USERNAME": "admin",
+ "SUPER_PASSWORD": "admin"
+}
+
+
+for key, value in DEFAULT_ENVS.items():
+ os.environ.setdefault(key, value)
+
+
+class SiteManager:
+ """
+ Manages the preparation and serving of the website.
+
+ Handles both development and production environments.
+
+ Usage:
+ manage.py run [option]...
+
+ Options:
+ --debug Runs a development server with debug mode enabled.
+ --silent Sets no output in console for preparation commands.
+ --verbose Sets verbose output for preparation commands.
+ """
+
+ def __init__(self, args: List[str]):
+ self.debug = "--debug" in args
+ self.silent = "--silent" in args
+
+ if self.silent:
+ self.verbosity = 0
+ else:
+ self.verbosity = 2 if "--verbose" in args else 1
+
+ if self.verbosity and self.debug:
+ os.environ.setdefault("DEBUG", "true")
+ print("Starting in debug mode.")
+
+ @staticmethod
+ def create_superuser() -> None:
+ """Create a default django admin super user in development environments."""
+ print("Creating a superuser.")
+
+ name = os.environ["SUPER_USERNAME"]
+ password = os.environ["SUPER_PASSWORD"]
+ user = get_user_model()
+
+ if user.objects.filter(username=name).exists():
+ return print('Admin superuser already exists')
+
+ user.objects.create_superuser(name, '', password)
+
+ @staticmethod
+ def wait_for_postgres() -> None:
+ """Wait for the PostgreSQL database specified in DATABASE_URL."""
+ print("Waiting for PostgreSQL database.")
+
+ # Get database URL based on environmental variable passed in compose
+ database_url = os.environ["DATABASE_URL"]
+ match = re.search(r"@(\w+):(\d+)/", database_url)
+ if not match:
+ raise OSError("Valid DATABASE_URL environmental variable not found.")
+ domain = match.group(1)
+ port = int(match.group(2))
+
+ # Attempt to connect to the database socket
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ while True:
+ try:
+ # Ignore 'incomplete startup packet'
+ s.connect((domain, port))
+ s.shutdown(socket.SHUT_RDWR)
+ print("Database is ready.")
+ break
+ except socket.error:
+ print("Not ready yet, retrying.")
+ time.sleep(0.5)
+
+ def prepare_server(self) -> None:
+ """Perform preparation tasks before running the server."""
+ django.setup()
+
+ if self.debug:
+ self.wait_for_postgres()
+ self.create_superuser()
+
+ print("Applying migrations.")
+ call_command("migrate", verbosity=self.verbosity)
+ print("Collecting static files.")
+ call_command("collectstatic", interactive=False, clear=True, verbosity=self.verbosity)
+
+ def run_server(self) -> None:
+ """Prepare and run the web server."""
+ in_reloader = os.environ.get('RUN_MAIN') == 'true'
+
+ # Prevent preparing twice when in debug mode due to reloader
+ if not self.debug or in_reloader:
+ self.prepare_server()
+
+ print("Starting server.")
+
+ # Create a superuser and run the development server
+ if self.debug:
+ call_command("runserver", "0.0.0.0:8000")
+ return
+
+ # Run uwsgi for production server
+ pyuwsgi.run(["--ini", "docker/uwsgi.ini"])
+
+
+def main() -> None:
"""Entry point for Django management script."""
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pydis_site.settings')
- try:
- from django.core.management import execute_from_command_line
- except ImportError as exc:
- raise ImportError(
- "Couldn't import Django. Are you sure it's installed and "
- "available on your PYTHONPATH environment variable? Did you "
- "forget to activate a virtual environment?"
- ) from exc
- execute_from_command_line(sys.argv)
+ # Use the custom site manager for launching the server
+ if len(sys.argv) > 1 and sys.argv[1] == "run":
+ SiteManager(sys.argv).run_server()
+
+ # Pass any others directly to standard management commands
+ else:
+ execute_from_command_line(sys.argv)
if __name__ == '__main__':