Author SHA1 Message Date
westfarn 00686ba53f Update workflow to use server-infra 2026-07-08 06:02:17 -05:00
westfarnandCursor d2f2409a53 fix(static): don't 500 on missing manifest entries at runtime
CI / test (pull_request) Successful in 10s
Unit Tests / test (pull_request) Successful in 9s
Templates reference public/img/logo.png (favicon, brand logo, social
share images) which isn't present in the repo. With the strict manifest
storage this raised ValueError at request time -> HTTP 500. Override
stored_name (and set manifest_strict=False) to fall back to the plain
name so a missing static degrades to a broken asset instead of a 500,
matching the previous non-manifest behaviour.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 13:18:49 -05:00
westfarnandCursor 5d378b7b19 fix(settings): accept JSON-array env lists in env_list
CI / test (pull_request) Successful in 10s
Unit Tests / test (pull_request) Successful in 9s
The production .env stores DJANGO_ALLOWED_HOSTS as a JSON array (legacy
format), but env_list only split on commas, yielding broken entries like
'["aimloperations.com"' and causing DisallowedHost (HTTP 400) for every
request. Parse JSON arrays as well as comma-separated values.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 13:13:57 -05:00
westfarnandCursor 5899c1f14f fix(static): tolerate missing asset references in collectstatic
CI / test (pull_request) Successful in 10s
Unit Tests / test (pull_request) Successful in 10s
WhiteNoise's manifest storage strictly resolves every referenced file
during collectstatic, including sourceMappingURL comments in vendored
JS bundles. A missing .map (financial/js/.../dashboard-free.js.map)
broke the prod container at startup. Add TolerantManifestStaticFilesStorage
which leaves unresolved references untouched instead of raising.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 13:10:51 -05:00
westfarn a4b37a0bb0 g
CI / test (pull_request) Successful in 10s
Unit Tests / test (pull_request) Successful in 10s
2026-07-07 11:20:15 -05:00
4 changed files with 61 additions and 13 deletions
+8 -7
View File
@@ -54,11 +54,12 @@ jobs:
if: gitea.event.workflow_run.conclusion == 'success' && gitea.event.workflow_run.event == 'push' if: gitea.event.workflow_run.conclusion == 'success' && gitea.event.workflow_run.event == 'push'
runs-on: self-hosted runs-on: self-hosted
needs: [test, docker] needs: [test, docker]
env:
SERVER_INFRA_ROOT: /home/westfarn/Documents/repos/server-infra
steps: steps:
- name: Checkout - name: Deploy company_site prod
uses: actions/checkout@v4 run: |
with: "${SERVER_INFRA_ROOT}/scripts/deploy.sh" \
ref: ${{ gitea.event.workflow_run.head_sha }} --app company_site \
--env prod \
- name: Deploy to live site --ref "${{ gitea.event.workflow_run.head_sha }}"
run: bash scripts/deploy.sh "${{ gitea.workspace }}"
+10 -5
View File
@@ -13,13 +13,18 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Python environment - name: Install uv
run: | run: |
python3 -m venv .venv curl -LsSf https://astral.sh/uv/install.sh | sh
.venv/bin/pip install --upgrade pip echo "$HOME/.local/bin" >> "$GITHUB_PATH"
.venv/bin/pip install -r requirements.txt
- name: Install dependencies
run: uv sync --frozen
- name: Run unit tests - name: Run unit tests
env:
DJANGO_ENV: dev
DJANGO_SECRET_KEY: test-secret-key
run: | run: |
cd company_site cd company_site
../.venv/bin/python manage.py test uv run python manage.py test
+11 -1
View File
@@ -1,5 +1,6 @@
"""Shared Django settings for all environments.""" """Shared Django settings for all environments."""
import json
import os import os
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse from urllib.parse import urlparse
@@ -22,6 +23,15 @@ def env_list(key: str, default: str = "") -> list[str]:
value = os.environ.get(key, default) value = os.environ.get(key, default)
if not value: if not value:
return [] return []
value = value.strip()
# Accept a JSON array (e.g. '["a","b"]') as well as a comma-separated list.
if value.startswith("["):
try:
parsed = json.loads(value)
except ValueError:
parsed = None
if isinstance(parsed, list):
return [str(item).strip() for item in parsed if str(item).strip()]
return [item.strip() for item in value.split(",") if item.strip()] return [item.strip() for item in value.split(",") if item.strip()]
@@ -147,7 +157,7 @@ STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles" STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = { STORAGES = {
"staticfiles": { "staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", "BACKEND": "company_site.storage.TolerantManifestStaticFilesStorage",
}, },
} }
+32
View File
@@ -0,0 +1,32 @@
"""Custom static files storage.
WhiteNoise's manifest storage post-processes JS/CSS during ``collectstatic`` and
strictly resolves every referenced file, including ``sourceMappingURL`` comments
in vendored bundles. Some third-party assets reference ``.map`` files that are
not shipped, which makes ``collectstatic`` fail hard.
``TolerantManifestStaticFilesStorage`` leaves such unresolved references
untouched instead of raising, so a missing source map can't break the build.
"""
from whitenoise.storage import CompressedManifestStaticFilesStorage
class TolerantManifestStaticFilesStorage(CompressedManifestStaticFilesStorage):
# Don't 500 at runtime when a {% static %} reference isn't in the manifest;
# fall back to the plain name (mirrors non-manifest storage behaviour).
manifest_strict = False
def _stored_name(self, name, hashed_files):
"""Tolerate missing references during collectstatic post-processing."""
try:
return super()._stored_name(name, hashed_files)
except ValueError:
return name
def stored_name(self, name):
"""Tolerate missing manifest entries at request time."""
try:
return super().stored_name(name)
except ValueError:
return name