aboutsummaryrefslogtreecommitdiffstats
path: root/manage.py
diff options
context:
space:
mode:
authorGravatar Leon Sandøy <[email protected]>2019-09-29 18:17:29 +0200
committerGravatar GitHub <[email protected]>2019-09-29 18:17:29 +0200
commited38ec2f79fab4758c1e708bb321dc23930f8fe0 (patch)
tree7a57dd244ea83ab8facbc0e573aabed6dd3b4dcb /manage.py
parentAdd static images for wiki contributing guides (#266) (diff)
parentMerge branch 'master' into new-managepy (diff)
Merge pull request #265 from python-discord/new-managepy
Custom `manage.py` Entry Point Script
Diffstat (limited to 'manage.py')
-rwxr-xr-xmanage.py162
1 files changed, 150 insertions, 12 deletions
diff --git a/manage.py b/manage.py
index b257ea65..44435de5 100755
--- a/manage.py
+++ b/manage.py
@@ -1,21 +1,159 @@
#!/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",
+ "DEFAULT_BOT_API_KEY": "badbot13m0n8f570f942013fc818f234916ca531",
+}
+
+
+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 minimal console output.
+ --verbose Sets verbose console output.
+ """
+
+ 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.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"]
+ bot_token = os.environ["DEFAULT_BOT_API_KEY"]
+ user = get_user_model()
+
+ # Get or create admin superuser.
+ if user.objects.filter(username=name).exists():
+ user = user.objects.get(username=name)
+ print('Admin superuser already exists.')
+ else:
+ user = user.objects.create_superuser(name, '', password)
+ print('Admin superuser created.')
+
+ # Setup a default bot token to connect with site API
+ from rest_framework.authtoken.models import Token
+ token, is_new = Token.objects.update_or_create(user=user)
+ if token.key != bot_token:
+ token.delete()
+ token, is_new = Token.objects.update_or_create(user=user, key=bot_token)
+ if is_new:
+ print(f"New bot token created: {token}")
+ else:
+ print(f"Existing bot token found: {token}")
+
+ @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)
+
+ attempts_left = 10
+ while attempts_left:
+ try:
+ # Ignore 'incomplete startup packet'
+ s.connect((domain, port))
+ s.shutdown(socket.SHUT_RDWR)
+ print("Database is ready.")
+ break
+ except socket.error:
+ attempts_left -= 1
+ print("Not ready yet, retrying.")
+ time.sleep(0.5)
+ else:
+ print("Database could not be found, exiting.")
+ sys.exit(1)
+
+ 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 dev mode due to reloader
+ if not self.debug or in_reloader:
+ self.prepare_server()
+
+ print("Starting server.")
+
+ # 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__':