Compare commits

...

4 commits

Author SHA1 Message Date
552c74f626
Merge branch 'dev' (staging/no-tag) 2025-05-23 21:55:17 -07:00
b26ca10489
fix(docker): Add missing ALLOWED_HOSTS environment variables to docker-compose_web.yml and docker-compose_staging.yml to prevent security errors. 2025-05-23 21:54:55 -07:00
acbbc33efa
fix(static): For now, replace @ symbols from static js filenames as granian doesn't handle URL-encoded static paths properly yet. But the static file handling is faster with granian, so we want to keep it. 2025-05-23 21:35:52 -07:00
51de3c7a6d
feat(dev): Enable hot reloading and streamline local development
This commit significantly improves the local development experience by enabling hot reloading for the Django application. This is achieved by installing the project as an editable package within the Docker services.

Key changes:

- **Hot Reloading:**
    - Modified `docker-compose.yml` for `web` and `celery` services to use `uv pip install --editable . --no-deps`.
    - Mounted the project root (`./`) to `/code` in `web` and `celery` services to facilitate the editable install.
- **Docker & Build Enhancements:**
    - Added `uv` binary to stage-1 in the `Dockerfile` for faster package operations.
    - Adjusted file permissions in `Dockerfile` during the app copy.
    - Set `DEBUG=true` for the `web` service in `docker-compose.yml` for easier local debugging.
    - Changed `restart` policy to `unless-stopped` for `web` and `celery` dev services.
    - Added a healthcheck for the `redis` service in the dev `docker-compose.yml`.
- **Code & Script Cleanup:**
    - Removed the custom `HealthCheckView` from the `home` app, as health checks are now handled by django-health-checks.
    - Updated paths and commands in `scripts/entrypoint.sh`, `scripts/prebuild.sh`, and `scripts/reset-db_make-migrations_seed-data.sh` to align with the new setup and remove obsolete steps (e.g., db cache table creation; we now use redis).
2025-05-23 21:19:33 -07:00
15 changed files with 46 additions and 63 deletions

View file

@ -86,8 +86,9 @@ EOT
# See <https://hynek.me/articles/docker-signals/>. # See <https://hynek.me/articles/docker-signals/>.
STOPSIGNAL SIGINT STOPSIGNAL SIGINT
COPY --from=build --chown=app:app /app /app COPY --from=build --chown=app:app --chmod=u+rw /app /app
COPY --from=ghcr.io/astral-sh/uv:0.7.2 /uv /app/bin/uv
COPY --chown=app:app --chmod=700 /scripts/entrypoint.sh /entrypoint.sh COPY --chown=app:app --chmod=700 /scripts/entrypoint.sh /entrypoint.sh
COPY --chown=app:app --chmod=700 /scripts/deploy.sh /deploy.sh COPY --chown=app:app --chmod=700 /scripts/deploy.sh /deploy.sh
COPY --chown=app:app --chmod=700 /manage.py /app/manage.py COPY --chown=app:app --chmod=700 /manage.py /app/manage.py

View file

@ -1,30 +1,41 @@
services: services:
web: web:
build: . build: .
#command: ["django-admin", "runserver", "0.0.0.0:8000"] command: bash -c "cd /code && uv pip install --editable . --no-deps && python manage.py runserver 0.0.0.0:8000"
ports: ports:
- 8000:8000 - "8000:8000"
restart: always restart: unless-stopped
environment:
- DEBUG=true
volumes: volumes:
- ./seed:/seed:ro - ./seed:/seed:ro
# DANGEROUS DUE TO DOCKERFILE PACKAGE BUILDING/INSTALLATION - ./:/code
# will need to use editable package instead somehow
#- ./src/pkmntrade_club:/app/lib/python3.12/site-packages/pkmntrade_club:ro
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
redis:
condition: service_started
celery: celery:
build: . build: .
command: ["celery", "-A", "pkmntrade_club.django_project", "worker", "-l", "INFO", "-B", "-E"] command: bash -c "cd /code && uv pip install --editable . --no-deps && celery -A pkmntrade_club.django_project worker -l INFO -B -E"
restart: always restart: unless-stopped
volumes:
- ./:/code
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
redis:
condition: service_started
redis: redis:
image: redis:latest image: redis:latest
restart: always restart: always
ports: ports:
- 6379:6379 - 6379:6379
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
db: db:
image: postgres:16 image: postgres:16
restart: always restart: always
@ -37,6 +48,5 @@ services:
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
volumes: volumes:
postgres_data: postgres_data:

View file

@ -8,10 +8,8 @@ fi
/deploy.sh /deploy.sh
# show command version before running it
$1 --version
if [ "$1" == "granian" ]; then if [ "$1" == "granian" ]; then
granian --version
echo "Appending static files path to granian command (requires granian >= 2.3.0)" echo "Appending static files path to granian command (requires granian >= 2.3.0)"
STATIC_ROOT=$(python -c 'import os; import pkmntrade_club; from django.conf import settings; print(settings.STATIC_ROOT)') STATIC_ROOT=$(python -c 'import os; import pkmntrade_club; from django.conf import settings; print(settings.STATIC_ROOT)')
set -- "$@" --static-path-mount "$STATIC_ROOT" set -- "$@" --static-path-mount "$STATIC_ROOT"

View file

@ -1,11 +1,12 @@
#!/bin/bash #!/bin/bash
cd src/pkmntrade_club/
# Remove all files in staticfiles except .gitkeep # Remove all files in staticfiles except .gitkeep
if [ -d "staticfiles" ]; then if [ -d "staticfiles" ]; then
find staticfiles -type f ! -name '.gitkeep' -delete find staticfiles -type f ! -name '.gitkeep' -delete
find staticfiles -type d -empty -delete
fi fi
# Build the tailwind theme css # Build the tailwind theme css
cd src/pkmntrade_club/theme/static_src cd theme/static_src
npm install . && npm run build npm install . && npm run build

View file

@ -9,18 +9,14 @@ source .env
set +a set +a
uv run manage.py makemigrations --noinput uv run manage.py makemigrations --noinput
echo "Resetting database... " echo "Resetting dev database... "
docker compose down \ docker compose down \
&& docker volume rm -f pkmntradeclub_postgres_data \ && docker volume rm -f pkmntradeclub_postgres_data \
&& ./scripts/rebuild-and-run.sh && ./scripts/rebuild-and-run.sh
# Wait for the database to be ready. # Wait for the database to be ready.
echo "Waiting 15 seconds for the database to be ready, and migrations to be autorun..." echo "Waiting 15 seconds for migrations to be auto-run..."
sleep 15 sleep 15
echo "Creating cache table..."
docker compose exec -it web bash -c "django-admin createcachetable django_cache"
echo "Loading seed data..." echo "Loading seed data..."
docker compose exec -it web bash -c "django-admin loaddata /seed/0*" docker compose exec -it web bash -c "django-admin loaddata /seed/0*"

View file

@ -10,13 +10,14 @@ services:
- DEBUG=False - DEBUG=False
- DISABLE_SIGNUPS=True - DISABLE_SIGNUPS=True
- PUBLIC_HOST=staging.pkmntrade.club - PUBLIC_HOST=staging.pkmntrade.club
- ALLOWED_HOSTS=staging.pkmntrade.club,127.0.0.1
labels: labels:
- "enable_gatekeeper=true" - "enable_gatekeeper=true"
deploy: deploy:
mode: replicated mode: replicated
replicas: 2 replicas: 2
# healthcheck: # healthcheck:
# test: ["CMD", "curl", "-f", "http://127.0.0.1:8000"] # test: ["CMD", "curl", "-f", "http://127.0.0.1:8000/health"]
# interval: 30s # interval: 30s
# timeout: 10s # timeout: 10s
# retries: 3 # retries: 3
@ -27,4 +28,5 @@ services:
- DEBUG=False - DEBUG=False
- DISABLE_SIGNUPS=True - DISABLE_SIGNUPS=True
- PUBLIC_HOST=staging.pkmntrade.club - PUBLIC_HOST=staging.pkmntrade.club
- ALLOWED_HOSTS=staging.pkmntrade.club,127.0.0.1
command: ["celery", "-A", "pkmntrade_club.django_project", "worker", "-l", "INFO", "-B", "-E"] command: ["celery", "-A", "pkmntrade_club.django_project", "worker", "-l", "INFO", "-B", "-E"]

View file

@ -9,18 +9,18 @@ services:
image: ghcr.io/xe/x/httpdebug image: ghcr.io/xe/x/httpdebug
entrypoint: ["/ko-app/httpdebug", "--bind", ":8000"] entrypoint: ["/ko-app/httpdebug", "--bind", ":8000"]
#image: badbl0cks/pkmntrade-club:stable #image: badbl0cks/pkmntrade-club:stable
#command: ["granian", "--interface", "wsgi", "pkmntrade_club.django_project.wsgi:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", "--workers-kill-timeout", "180", "--access-log"]
environment: environment:
- DEBUG=False - DEBUG=False
- DISABLE_SIGNUPS=True - DISABLE_SIGNUPS=True
- PUBLIC_HOST=pkmntrade.club - PUBLIC_HOST=pkmntrade.club
- ALLOWED_HOSTS=pkmntrade.club,127.0.0.1
labels: labels:
- "enable_gatekeeper=true" - "enable_gatekeeper=true"
deploy: deploy:
mode: replicated mode: replicated
replicas: 4 replicas: 4
# healthcheck: # healthcheck:
# test: ["CMD", "curl", "-f", "http://127.0.0.1:8000"] # test: ["CMD", "curl", "-f", "http://127.0.0.1:8000/health"]
# interval: 30s # interval: 30s
# timeout: 10s # timeout: 10s
# retries: 3 # retries: 3
@ -32,4 +32,5 @@ services:
# - DEBUG=False # - DEBUG=False
# - DISABLE_SIGNUPS=True # - DISABLE_SIGNUPS=True
# - PUBLIC_HOST=pkmntrade.club # - PUBLIC_HOST=pkmntrade.club
# - ALLOWED_HOSTS=pkmntrade.club,127.0.0.1
# command: ["celery", "-A", "pkmntrade_club.django_project", "worker", "-l", "INFO", "-B", "-E"] # command: ["celery", "-A", "pkmntrade_club.django_project", "worker", "-l", "INFO", "-B", "-E"]

View file

@ -1,6 +1,6 @@
from django.urls import path from django.urls import path
from .views import HomePageView, HealthCheckView from .views import HomePageView
urlpatterns = [ urlpatterns = [
path("", HomePageView.as_view(), name="home"), path("", HomePageView.as_view(), name="home"),

View file

@ -139,29 +139,3 @@ class HomePageView(TemplateView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Override get method to add caching""" """Override get method to add caching"""
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
class HealthCheckView(View):
def get(self, request, *args, **kwargs):
try:
from django.db import connection
connection.cursor().execute("SELECT 1")
except Exception as e:
return HttpResponse("Database connection failed", status=500)
try:
from pkmntrade_club.trades.models import TradeOffer
with contextlib.redirect_stdout(None):
print(TradeOffer.objects.count())
except Exception as e:
return HttpResponse("DB models not reachable, but db is reachable", status=500)
try:
from django.core.cache import cache
cache.set("test", "test")
with contextlib.redirect_stdout(None):
print(cache.get("test"))
except Exception as e:
return HttpResponse("Cache not reachable", status=500)
return HttpResponse("OK/HEALTHY")

View file

@ -38,8 +38,8 @@
<link rel="stylesheet" href="{% static 'css/base.css' %}"> <link rel="stylesheet" href="{% static 'css/base.css' %}">
<!-- Floating UI --> <!-- Floating UI -->
<script src="{% static 'js/floating-ui_core@1.6.9.9.min.js' %}"></script> <script src="{% static 'js/floating-ui_core-1.6.9.9.min.js' %}"></script>
<script src="{% static 'js/floating-ui_dom@1.6.13.13.min.js' %}"></script> <script src="{% static 'js/floating-ui_dom-1.6.13.13.min.js' %}"></script>
<script defer src="{% static 'js/card-multiselect.js' %}"></script> <script defer src="{% static 'js/card-multiselect.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/card-multiselect.css' %}"> <link rel="stylesheet" href="{% static 'css/card-multiselect.css' %}">
@ -130,13 +130,13 @@
</div> </div>
<!-- Alpine Plugins --> <!-- Alpine Plugins -->
<script defer src="{% static 'js/alpinejs.collapse@3.14.8.min.js' %}"></script> <script defer src="{% static 'js/alpinejs.collapse-3.14.8.min.js' %}"></script>
<!-- Alpine Core --> <!-- Alpine Core -->
<script defer src="{% static 'js/alpinejs@3.14.8.min.js' %}"></script> <script defer src="{% static 'js/alpinejs-3.14.8.min.js' %}"></script>
<!-- 100% privacy-first, no tracking analytics --> <!-- Goatcounter: 100% privacy-first, no tracking analytics -->
<script data-goatcounter="https://stats.pkmntrade.club/count" async src="/static/js/count@v4.js"></script> <script data-goatcounter="https://stats.pkmntrade.club/count" async src="{% static 'js/count-v4.js' %}"></script>
{% block javascript %}{% endblock %} {% block javascript %}{% endblock %}
</body> </body>