Skip to content

Commit bfdfd0a

Browse files
committed
feat(dev): make dev super fast
Reduces average page load time from over 5 sec to 0.5 sec. Restarts web server on crash (due to python errors while coding). Disables DEBUG. Uses WhiteNoise for static files.
1 parent b03c02d commit bfdfd0a

File tree

9 files changed

+124
-35
lines changed

9 files changed

+124
-35
lines changed

Ion.egg-info/SOURCES.txt

+3
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ intranet/apps/dataimport/management/commands/import_students.py
401401
intranet/apps/dataimport/management/commands/import_users.py
402402
intranet/apps/dataimport/management/commands/year_cleanup.py
403403
intranet/apps/dataimport/migrations/__init__.py
404+
intranet/apps/django/management/commands/__init__.py
405+
intranet/apps/django/management/commands/run.py
404406
intranet/apps/eighth/Hybrid-README.rst
405407
intranet/apps/eighth/__init__.py
406408
intranet/apps/eighth/admin.py
@@ -3795,6 +3797,7 @@ migrations/__init__.py
37953797
scripts/build_docs.sh
37963798
scripts/build_ensure_no_changes.sh
37973799
scripts/build_sources.sh
3800+
scripts/dev_autoupdate_static.sh
37983801
scripts/export_fixtures.sh
37993802
scripts/format.sh
38003803
scripts/get_ldif.py

config/docker/docker-compose.yml

+41-26
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,88 @@
11
version: '3.9'
22

3-
43
services:
54
redis:
65
container_name: server
76
image: redis:latest
87
networks:
9-
- intranet_net
8+
- intranet_net
109
expose:
11-
- "6379"
10+
- "6379"
1211

1312
postgres:
1413
container_name: database
1514
image: postgres:latest
1615
networks:
17-
- intranet_net
16+
- intranet_net
1817
expose:
19-
- "5432"
18+
- "5432"
2019
environment:
21-
- POSTGRES_DB=ion
22-
- POSTGRES_USER=ion
23-
- POSTGRES_PASSWORD=pwd
20+
- POSTGRES_DB=ion
21+
- POSTGRES_USER=ion
22+
- POSTGRES_PASSWORD=pwd
2423
volumes:
25-
- pgdata:/var/lib/postgresql/data
24+
- pgdata:/var/lib/postgresql/data
2625

2726
application:
2827
container_name: intranet
2928
image: application
3029
networks:
31-
- intranet_net
32-
entrypoint: ["/bin/sh", "-c" , "git config --global --add safe.directory /ion && python3 config/docker/entrypoint.py && chmod +x config/docker/entrypoint.sh && config/docker/entrypoint.sh"]
30+
- intranet_net
31+
entrypoint:
32+
[
33+
"/bin/sh",
34+
"-c",
35+
"git config --global --add safe.directory /ion && \
36+
/bin/sh config/docker/entrypoint.sh"
37+
]
3338
ports:
34-
- 8080:8080
39+
- 8080:8080
3540
build:
3641
context: ../../
3742
dockerfile: ./config/docker/Dockerfile
3843
volumes:
39-
- ../../:/ion:z
44+
- ../../:/ion:z
4045
depends_on:
41-
- redis
42-
- postgres
46+
- redis
47+
- postgres
4348

4449
celery:
4550
container_name: celery
4651
image: application
4752
networks:
48-
- intranet_net
49-
entrypoint: ["/bin/sh", "-c" , "git config --global --add safe.directory /ion && pipenv run celery --app intranet worker -l info --without-gossip --without-mingle --without-heartbeat -Ofair"]
53+
- intranet_net
54+
entrypoint:
55+
[
56+
"/bin/sh",
57+
"-c",
58+
"git config --global --add safe.directory /ion && \
59+
pipenv run celery --app intranet worker -l info --without-gossip --without-mingle --without-heartbeat -Ofair"
60+
]
5061
depends_on:
51-
- application
62+
- application
5263
volumes:
53-
- ../../:/ion:z
64+
- ../../:/ion:z
5465

5566
celerybeat:
5667
container_name: celerybeat
5768
image: application
5869
networks:
59-
- intranet_net
60-
entrypoint: ["/bin/sh", "-c" , "git config --global --add safe.directory /ion && pipenv run celery --app intranet beat -l info"]
70+
- intranet_net
71+
entrypoint:
72+
[
73+
"/bin/sh",
74+
"-c",
75+
"git config --global --add safe.directory /ion && \
76+
pipenv run celery --app intranet beat -l info"
77+
]
6178
depends_on:
62-
- application
79+
- application
6380
volumes:
64-
- ../../:/ion:z
65-
81+
- ../../:/ion:z
6682

6783
networks:
6884
intranet_net:
69-
driver: bridge
70-
85+
driver: bridge
7186

7287
volumes:
7388
pgdata:

config/docker/entrypoint.sh

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
#!/bin/sh
22
echo "---- Running entrypoint script ----"
33

4-
echo "Starting web server..."
5-
echo ""
4+
echo "Performing pre-startup tasks..."
5+
export DEBUG=FALSE # HUGE performance benefits. Manually enable if you need to debug.
6+
python3 config/docker/entrypoint.py & # For initial setup
7+
sass --watch intranet/static/css:intranet/collected_static/css & # Automatically compile modified scss files
68

9+
echo "Starting web server..."
710
cat << END
11+
812
██╗ ██╗███████╗██╗ ██████╗ ██████╗ ███╗ ███╗███████╗
913
██║ ██║██╔════╝██║ ██╔════╝██╔═══██╗████╗ ████║██╔════╝
1014
██║ █╗ ██║█████╗ ██║ ██║ ██║ ██║██╔████╔██║█████╗
@@ -18,6 +22,12 @@ cat << END
1822
██║ ██║ ██║ ██║██║ ██║██║╚██╗██║╚═╝
1923
██║ ╚██████╔╝ ██║╚██████╔╝██║ ╚████║██╗
2024
╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝
25+
2126
END
2227

23-
python3 manage.py runserver 0.0.0.0:8080
28+
# Wrap the run command in a loop so that it restarts if it crashes, e.g. due to a syntax error
29+
while : # while true
30+
do
31+
python3 manage.py run 0.0.0.0:8080 # Custom run command that skips system checks for performance
32+
sleep 1
33+
done

intranet/apps/django/management/commands/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django.contrib.staticfiles.management.commands.runserver import Command as RunserverCommand
2+
3+
4+
class Command(RunserverCommand):
5+
"""Run the development server but skip checks for performance."""
6+
7+
def check(self, *args, **kwargs):
8+
self.stdout.write("Skipping system checks.")
9+
10+
def check_migrations(self, *args, **kwargs):
11+
self.stdout.write("Skipping migrations checks.\nRun 'python3 manage.py check' to perform checks.\n\n")

intranet/middleware/templates.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ def __init__(self, get_response):
1515
def __call__(self, request):
1616
"""Process the response and strip extra newlines from HTML."""
1717
response = self.get_response(request)
18-
is_html = response["Content-Type"] == "text/html" or response["Content-Type"].startswith("text/html;")
18+
is_html = (
19+
response["Content-Type"] == "text/html" or response["Content-Type"].startswith("text/html;") if "Content-Type" in response else False
20+
) # noqa: E501 pylint: disable=line-too-long
1921
if is_html and settings.DEBUG:
2022
response.content = re.sub(r"\n(\s*)\n", "\n", response.content.decode())
2123
response.content = re.sub(r"^(\s*)\n", "", response.content.decode())
@@ -35,7 +37,9 @@ def __init__(self, get_response):
3537

3638
def __call__(self, request):
3739
response = self.get_response(request)
38-
is_html = response["Content-Type"] == "text/html" or response["Content-Type"].startswith("text/html;")
40+
is_html = (
41+
response["Content-Type"] == "text/html" or response["Content-Type"].startswith("text/html;") if "Content-Type" in response else False
42+
) # noqa: E501 pylint: disable=line-too-long
3943
if is_html and request.path.startswith("/eighth/admin"):
4044
replacement = """</select>
4145
<div class="selectize-control selectize-loading">
@@ -57,7 +61,9 @@ def __init__(self, get_response):
5761

5862
def __call__(self, request):
5963
response = self.get_response(request)
60-
is_html = response["Content-Type"] == "text/html" or response["Content-Type"].startswith("text/html;")
64+
is_html = (
65+
response["Content-Type"] == "text/html" or response["Content-Type"].startswith("text/html;") if "Content-Type" in response else False
66+
) # noqa: E501 pylint: disable=line-too-long
6167
if is_html:
6268
response.content = re.sub(r'<a(.*href ?= ?[\'"]http.*)>', r'<a rel="noopener noreferrer"\1>', response.content.decode())
6369
return response

intranet/settings/__init__.py

+14
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@
243243
# Example: "/home/media/media.lawrence.com/static/"
244244
#
245245
# This is the folder that Nginx serves as /static in production
246+
# and WhiteNoise serves in development.
246247
STATIC_ROOT = os.path.join(PROJECT_ROOT, "collected_static")
247248

248249
# URL prefix for static files.
@@ -433,6 +434,14 @@
433434
"django_referrer_policy.middleware.ReferrerPolicyMiddleware", # Sets the Referrer-Policy header
434435
]
435436

437+
if not PRODUCTION and not DEBUG:
438+
# Serve static files using WhiteNoise in development if DEBUG is False
439+
# See http://whitenoise.evans.io/en/stable/django.html
440+
WHITENOISE_AUTOREFRESH = True
441+
WHITENOISE_USE_FINDERS = True
442+
WHITENOISE_MAX_AGE = 0
443+
MIDDLEWARE += ["whitenoise.middleware.WhiteNoiseMiddleware"]
444+
436445
# URLconf at urls.py
437446
ROOT_URLCONF = "intranet.urls"
438447

@@ -579,6 +588,7 @@ def get_month_seconds():
579588
"intranet.apps.auth",
580589
"intranet.apps.bus",
581590
"intranet.apps.cslapps",
591+
"intranet.apps.django",
582592
"intranet.apps.eighth",
583593
"intranet.apps.events",
584594
"intranet.apps.groups",
@@ -733,6 +743,10 @@ def get_log(name): # pylint: disable=redefined-outer-name; 'name' is used as th
733743
"SHOW_TOOLBAR_CALLBACK": "intranet.utils.helpers.debug_toolbar_callback",
734744
}
735745

746+
# Disable all panels by default in development for performance
747+
if not PRODUCTION:
748+
DEBUG_TOOLBAR_CONFIG["DISABLE_PANELS"] = [panel for panel, _ in _panels]
749+
736750
DEBUG_TOOLBAR_PANELS = [t[0] for t in _panels]
737751

738752
# Add middleware

intranet/utils/helpers.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,19 @@ def parse_db_url(db_url):
4040

4141

4242
def debug_toolbar_callback(request):
43-
"""Show the debug toolbar to those with the Django staff permission, excluding the Eighth Period
44-
office."""
43+
"""
44+
In development:
45+
Show the debug toolbar to all users.
46+
In production:
47+
Show the debug toolbar to those with the Django staff permission, excluding the Eighth Period
48+
office.
49+
"""
50+
51+
if not settings.PRODUCTION:
52+
return True
4553

4654
if request.headers.get("x-requested-with") == "XMLHttpRequest":
4755
return False
48-
4956
if not hasattr(request, "user"):
5057
return False
5158
if not request.user.is_authenticated:

scripts/dev_autoupdate_static.sh

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
# FOR DEVELOPMENT ONLY
3+
# Automatically copies modified static files in intranet/static to intranet/collected_static
4+
# Run from root ion directory, e.g. ./scripts/dev_autoupdate_static.sh
5+
# Use this when WHITENOISE_USE_FINDERS is set to False in settings
6+
7+
cd intranet/static
8+
9+
# CSS
10+
sass --watch css:../collected_static/css &
11+
12+
# Everything else
13+
inotifywait --format '%w%f %e' -rm -e modify -e create -e delete --exclude '\.scss$' . |
14+
while read -r file action; do
15+
file=${file#./} # Remove leading ./ from file path
16+
if [ "$action" = "CREATE" ] || [ "$action" = "MODIFY" ]; then
17+
cp "$file" ../collected_static/"$file"
18+
echo "Copied $file to ../collected_static/$file"
19+
elif [ "$action" = "DELETE" ]; then
20+
rm ../collected_static/"$file"
21+
echo "Deleted ../collected_static/$file"
22+
fi
23+
done

0 commit comments

Comments
 (0)