Skip to content

Commit 747a7b9

Browse files
committed
Pull from upstream/main and resolve conflicts.
2 parents d106260 + 86cdb83 commit 747a7b9

File tree

93 files changed

+1921
-828
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+1921
-828
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ max-line-length=100
33
docstring-convention=all
44
import-order-style=pycharm
55
application_import_names=pydis_site
6-
exclude=__pycache__, venv, .venv, **/migrations/**, .cache/
6+
exclude=__pycache__, venv, .venv, **/migrations/**, .cache/, gunicorn.conf.py
77
ignore=
88
B311,W503,E226,S311,T000
99
# Missing Docstrings
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
## Summary
2+
<!-- Please provide a brief description of this PR along with the related issue it fixes -->
3+
4+
5+
## Description of changes
6+
<!-- Describe what changes you've made, and how you implemented them -->
7+
8+
9+
### I confirm I have:
10+
<!-- Replace [ ] below with [X] to check the boxes -->
11+
- [ ] Joined the [Python Discord community](discord.gg/python)
12+
- [ ] Read the [Code of Conduct](https://www.pydis.com/pages/code-of-conduct) and agree to it
13+
- [ ] I have discussed implementing this feature on the relevant service (Discord, GitHub, etc.)
14+
15+
16+
### I have changed API models and I ensure I have:
17+
<!-- Please remove this section if you haven't edited files under pydis_site/apps/api/models -->
18+
- [ ] Opened a PR updating the model on the [API GitHub Repository](https://github.com/python-discord/api)
19+
20+
**OR**
21+
22+
- [ ] Opened an issue on the [API GitHub Repository](https://github.com/python-discord/api) explaining what changes need to be made

.github/workflows/deploy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
uses: Azure/k8s-deploy@v1
4242
with:
4343
manifests: |
44-
site/deployment.yaml
44+
namespaces/default/site/deployment.yaml
4545
images: 'ghcr.io/python-discord/site:${{ steps.sha_tag.outputs.tag }}'
4646
kubectl-version: 'latest'
4747

.github/workflows/static-preview.yaml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Build & Publish Static Preview
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
build:
11+
name: Build Static Preview
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
17+
# Create a commit SHA-based tag for the container repositories
18+
- name: Create SHA Container Tag
19+
id: sha_tag
20+
run: |
21+
tag=$(cut -c 1-7 <<< $GITHUB_SHA)
22+
echo "::set-output name=tag::$tag"
23+
24+
- name: Set up Docker Buildx
25+
uses: docker/setup-buildx-action@v1
26+
27+
- name: Login to Github Container Registry
28+
uses: docker/login-action@v1
29+
with:
30+
registry: ghcr.io
31+
username: ${{ github.repository_owner }}
32+
password: ${{ secrets.GITHUB_TOKEN }}
33+
34+
# Build the container, including an inline cache manifest to
35+
# allow us to use the registry as a cache source.
36+
- name: Build Docker Image (Main)
37+
uses: docker/build-push-action@v2
38+
if: github.ref == 'refs/heads/main'
39+
with:
40+
context: .
41+
push: true
42+
cache-from: type=registry,ref=ghcr.io/python-discord/static-site:latest
43+
cache-to: type=inline
44+
tags: |
45+
ghcr.io/python-discord/static-site:latest
46+
ghcr.io/python-discord/static-site:${{ steps.sha_tag.outputs.tag }}
47+
build-args: |
48+
git_sha=${{ github.sha }}
49+
STATIC_BUILD=TRUE
50+
51+
- name: Extract Build From Docker Image (Main)
52+
if: github.ref == 'refs/heads/main'
53+
run: |
54+
mkdir docker_build \
55+
&& docker run --entrypoint /bin/echo --name site \
56+
ghcr.io/python-discord/static-site:${{ steps.sha_tag.outputs.tag }} \
57+
&& docker cp site:/app docker_build/
58+
59+
# Build directly to a local folder
60+
- name: Build Docker Image (PR)
61+
uses: docker/build-push-action@v2
62+
if: github.ref != 'refs/heads/main'
63+
with:
64+
context: .
65+
push: false
66+
cache-from: type=registry,ref=ghcr.io/python-discord/static-site:latest
67+
outputs: type=local,dest=docker_build/
68+
build-args: |
69+
git_sha=${{ github.sha }}
70+
STATIC_BUILD=TRUE
71+
72+
- name: Upload Build
73+
uses: actions/upload-artifact@v2
74+
with:
75+
name: static-build
76+
path: docker_build/app/build/
77+
if-no-files-found: error

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,6 @@ staticfiles/
126126

127127
*.js.tmp
128128
log.*
129+
130+
# Local Netlify folder
131+
.netlify

Dockerfile

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.9.5-slim-buster
1+
FROM --platform=linux/amd64 python:3.9-slim-buster
22

33
# Allow service to handle stops gracefully
44
STOPSIGNAL SIGQUIT
@@ -24,6 +24,24 @@ ENV GIT_SHA=$git_sha
2424
# Copy the source code in last to optimize rebuilding the image
2525
COPY . .
2626

27+
# Set dummy variables so collectstatic can load settings.py
28+
RUN \
29+
# Set BUILDING_DOCKER to anything but undefined so settings.py
30+
# does not insert django_prometheus into the list of installed apps.
31+
# This prevents django_prometheus from attempting to connect to the database
32+
# when the collectstatic task is ran.
33+
BUILDING_DOCKER=yes \
34+
SECRET_KEY=dummy_value \
35+
DATABASE_URL=postgres://localhost \
36+
METRICITY_DB_URL=postgres://localhost \
37+
python manage.py collectstatic --noinput --clear
38+
39+
# Build static files if we are doing a static build
40+
ARG STATIC_BUILD=false
41+
RUN if [ $STATIC_BUILD = "TRUE" ] ; \
42+
then SECRET_KEY=dummy_value python manage.py distill-local build --traceback --force ; \
43+
fi
44+
2745
# Run web server through custom manager
2846
ENTRYPOINT ["python", "manage.py"]
2947
CMD ["run"]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This is all of the code that is responsible for maintaining [our website][9] and
1212
The website is built on Django and should be simple to set up and get started with.
1313
If you happen to run into issues with setup, please don't hesitate to open an issue!
1414

15-
If you're looking to contribute or play around with the code, take a look at [the wiki][10] or the [`docs` directory](docs). If you're looking for things to do, check out [our issues][11].
15+
If you're looking to contribute or play around with the code, take a look at [the wiki][10]. If you're looking for things to do, check out [our issues][11].
1616

1717
[1]: https://github.com/python-discord/site/workflows/Lint%20&%20Test/badge.svg?branch=main
1818
[2]: https://github.com/python-discord/site/actions?query=workflow%3A%22Lint+%26+Test%22+branch%3Amain

docker-compose.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ services:
1818
POSTGRES_DB: pysite
1919
POSTGRES_PASSWORD: pysite
2020
POSTGRES_USER: pysite
21+
healthcheck:
22+
test: ["CMD-SHELL", "pg_isready -U pysite"]
23+
interval: 2s
24+
timeout: 1s
25+
retries: 5
2126
volumes:
2227
- ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
2328

@@ -35,7 +40,8 @@ services:
3540
ports:
3641
- "127.0.0.1:8000:8000"
3742
depends_on:
38-
- postgres
43+
postgres:
44+
condition: service_healthy
3945
tty: true
4046
volumes:
4147
- .:/app:ro

gunicorn.conf.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
Configuration file for gunicorn.
3+
4+
Code taken from https://github.com/prometheus/client_python#multiprocess-mode-eg-gunicorn
5+
"""
6+
from prometheus_client import multiprocess
7+
8+
9+
def child_exit(server, worker) -> None:
10+
multiprocess.mark_process_dead(worker.pid)

manage.py

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
#!/usr/bin/env python
22
import os
3-
import re
4-
import socket
3+
import platform
54
import sys
6-
import time
7-
from typing import List
5+
from pathlib import Path
86

97
import django
108
from django.contrib.auth import get_user_model
@@ -42,7 +40,7 @@ class SiteManager:
4240
--verbose Sets verbose console output.
4341
"""
4442

45-
def __init__(self, args: List[str]):
43+
def __init__(self, args: list[str]):
4644
self.debug = "--debug" in args
4745
self.silent = "--silent" in args
4846

@@ -84,38 +82,6 @@ def create_superuser() -> None:
8482
else:
8583
print(f"Existing bot token found: {token}")
8684

87-
@staticmethod
88-
def wait_for_postgres() -> None:
89-
"""Wait for the PostgreSQL database specified in DATABASE_URL."""
90-
print("Waiting for PostgreSQL database.")
91-
92-
# Get database URL based on environmental variable passed in compose
93-
database_url = os.environ["DATABASE_URL"]
94-
match = re.search(r"@([\w.]+):(\d+)/", database_url)
95-
if not match:
96-
raise OSError("Valid DATABASE_URL environmental variable not found.")
97-
domain = match.group(1)
98-
port = int(match.group(2))
99-
100-
# Attempt to connect to the database socket
101-
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
102-
103-
attempts_left = 10
104-
while attempts_left:
105-
try:
106-
# Ignore 'incomplete startup packet'
107-
s.connect((domain, port))
108-
s.shutdown(socket.SHUT_RDWR)
109-
print("Database is ready.")
110-
break
111-
except socket.error:
112-
attempts_left -= 1
113-
print("Not ready yet, retrying.")
114-
time.sleep(0.5)
115-
else:
116-
print("Database could not be found, exiting.")
117-
sys.exit(1)
118-
11985
@staticmethod
12086
def set_dev_site_name() -> None:
12187
"""Set the development site domain in admin from default example."""
@@ -133,15 +99,19 @@ def prepare_server(self) -> None:
13399
"""Perform preparation tasks before running the server."""
134100
django.setup()
135101

136-
if self.debug:
137-
self.wait_for_postgres()
138-
139102
print("Applying migrations.")
140103
call_command("migrate", verbosity=self.verbosity)
141-
print("Collecting static files.")
142-
call_command("collectstatic", interactive=False, clear=True, verbosity=self.verbosity)
143104

144105
if self.debug:
106+
# In Production, collectstatic is ran in the Docker image
107+
print("Collecting static files.")
108+
call_command(
109+
"collectstatic",
110+
interactive=False,
111+
clear=True,
112+
verbosity=self.verbosity - 1
113+
)
114+
145115
self.set_dev_site_name()
146116
self.create_superuser()
147117

@@ -169,18 +139,32 @@ def run_server(self) -> None:
169139
"--preload",
170140
"-b", "0.0.0.0:8000",
171141
"pydis_site.wsgi:application",
172-
"--threads", "8",
173142
"-w", "2",
174-
"--max-requests", "1000",
175-
"--max-requests-jitter", "50",
176143
"--statsd-host", "graphite.default.svc.cluster.local:8125",
177144
"--statsd-prefix", "site",
145+
"--config", "file:gunicorn.conf.py"
178146
]
179147

180148
# Run gunicorn for the production server.
181149
gunicorn.app.wsgiapp.run()
182150

183151

152+
def clean_up_static_files(build_folder: Path) -> None:
153+
"""Recursively loop over the build directory and fix links."""
154+
for file in build_folder.iterdir():
155+
if file.is_dir():
156+
clean_up_static_files(file)
157+
elif file.name.endswith(".html"):
158+
# Fix parent host url
159+
new = file.read_text(encoding="utf-8").replace(f"//{os.getenv('PARENT_HOST')}", "")
160+
161+
# Fix windows paths if on windows
162+
if platform.system() == "Windows":
163+
new = new.replace("%5C", "/")
164+
165+
file.write_text(new, encoding="utf-8")
166+
167+
184168
def main() -> None:
185169
"""Entry point for Django management script."""
186170
# Use the custom site manager for launching the server
@@ -189,8 +173,23 @@ def main() -> None:
189173

190174
# Pass any others directly to standard management commands
191175
else:
176+
_static_build = "distill" in sys.argv[1]
177+
178+
if _static_build:
179+
# Build a static version of the site with no databases and API support
180+
os.environ["STATIC_BUILD"] = "True"
181+
if not os.getenv("PARENT_HOST"):
182+
os.environ["PARENT_HOST"] = "REPLACE_THIS.HOST"
183+
192184
execute_from_command_line(sys.argv)
193185

186+
if _static_build:
187+
# Clean up parent host in generated files
188+
for arg in sys.argv[2:]:
189+
if not arg.startswith("-"):
190+
clean_up_static_files(Path(arg))
191+
break
192+
194193

195194
if __name__ == '__main__':
196195
main()

0 commit comments

Comments
 (0)