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'
runs-on: self-hosted
needs: [test, docker]
env:
SERVER_INFRA_ROOT: /home/westfarn/Documents/repos/server-infra
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ gitea.event.workflow_run.head_sha }}
- name: Deploy to live site
run: bash scripts/deploy.sh "${{ gitea.workspace }}"
- name: Deploy company_site prod
run: |
"${SERVER_INFRA_ROOT}/scripts/deploy.sh" \
--app company_site \
--env prod \
--ref "${{ gitea.event.workflow_run.head_sha }}"
+10 -5
View File
@@ -13,13 +13,18 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python environment
- name: Install uv
run: |
python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -r requirements.txt
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Install dependencies
run: uv sync --frozen
- name: Run unit tests
env:
DJANGO_ENV: dev
DJANGO_SECRET_KEY: test-secret-key
run: |
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."""
import json
import os
from pathlib import Path
from urllib.parse import urlparse
@@ -22,6 +23,15 @@ def env_list(key: str, default: str = "") -> list[str]:
value = os.environ.get(key, default)
if not value:
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()]
@@ -147,7 +157,7 @@ STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"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