C1: deploy adelaide-baby-shower-app to ringtail k3s #349
1 changed files with 30 additions and 0 deletions
C1: pull shower sdist for vendored static (fixes /host/ 500)
The wheel ships config/ and shower/ only (per pyproject hatchling
config), leaving the repo's top-level static/ dir — Sortable.min.js,
cropper.min.js, cropper.min.css, prize-placeholder.svg — behind. At
runtime, host_dashboard.html's {% static 'css/cropper.min.css' %}
hits the manifest, CompressedManifestStaticFilesStorage raises
ValueError on the missing entry, /host/ returns 500.
Fix on the deploy side: fetch the sdist via fetchurl (pinned SRI hash
from forge PyPI), extract its top-level static/ subtree into a
non-FOD derivation, lay it down at /app/static in the image. The
local_settings shim adds /app/static to STATICFILES_DIRS so
collectstatic at boot picks the vendored assets up alongside the
Django admin's own static files.
Sdist URL is forge.ops.eblu.me/api/packages/... (tailnet) — matches
the just-landed edge block on forge.eblu.me/api/packages/*. The
nix-container-builder runner on ringtail is on the tailnet, so the
FOD fetch works.
App doesn't change. v1.0.3 is no longer needed for the static gap —
the wheel's "packages = [config, shower]" pattern stays as-is, and we
treat the sdist as the canonical bundle for the assets the wheel
intentionally omits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit
039d9b9507
|
|
@ -25,6 +25,28 @@ let
|
||||||
|
|
||||||
python = pkgs.python314;
|
python = pkgs.python314;
|
||||||
|
|
||||||
|
# The repo's top-level static/ directory (vendored Sortable + cropper
|
||||||
|
# JS/CSS, prize placeholder SVG) isn't shipped in the wheel — hatchling
|
||||||
|
# only packages config/ and shower/, leaving the repo-root static/
|
||||||
|
# behind. Pull the sdist (which contains the full source tree) and
|
||||||
|
# extract just the static/ subtree into the image as /app/static.
|
||||||
|
# local_settings adds it to STATICFILES_DIRS so collectstatic at boot
|
||||||
|
# picks it up alongside the Django admin's static files.
|
||||||
|
#
|
||||||
|
# Fetched from forge.ops.eblu.me (tailnet) because /api/packages/* is
|
||||||
|
# blocked at the fly edge — see fly/nginx.conf forge.eblu.me block.
|
||||||
|
# Hash is the upstream sha256 from forge PyPI's simple index.
|
||||||
|
showerSdist = pkgs.fetchurl {
|
||||||
|
name = "adelaide_baby_shower_app-${version}.tar.gz";
|
||||||
|
url = "https://forge.ops.eblu.me/api/packages/eblume/pypi/files/adelaide-baby-shower-app/${version}/adelaide_baby_shower_app-${version}.tar.gz";
|
||||||
|
hash = "sha256-nlCtlx9zuYaLoJZSckybLV5YPpA8vZamN96O3RXOstM=";
|
||||||
|
};
|
||||||
|
|
||||||
|
staticAssets = pkgs.runCommand "shower-static-assets-${version}" { } ''
|
||||||
|
${pkgs.gnutar}/bin/tar -xzf ${showerSdist} -C $TMPDIR
|
||||||
|
cp -r $TMPDIR/adelaide_baby_shower_app-${version}/static $out
|
||||||
|
'';
|
||||||
|
|
||||||
# Fixed-output derivation: pip-installs the app wheel + every transitive
|
# Fixed-output derivation: pip-installs the app wheel + every transitive
|
||||||
# dep into a single target dir. FODs get network access in exchange for
|
# dep into a single target dir. FODs get network access in exchange for
|
||||||
# a pinned output hash, which means the whole dependency closure is
|
# a pinned output hash, which means the whole dependency closure is
|
||||||
|
|
@ -147,11 +169,17 @@ let
|
||||||
# otherwise resolve to site-packages, scattering db.sqlite3 / media /
|
# otherwise resolve to site-packages, scattering db.sqlite3 / media /
|
||||||
# staticfiles into the venv. Pin them to /app/{data,media,data/staticfiles}.
|
# staticfiles into the venv. Pin them to /app/{data,media,data/staticfiles}.
|
||||||
localSettings = pkgs.writeText "local_settings.py" ''
|
localSettings = pkgs.writeText "local_settings.py" ''
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from config.settings import * # noqa: F401,F403
|
from config.settings import * # noqa: F401,F403
|
||||||
|
|
||||||
DATABASES["default"]["NAME"] = "/app/data/db.sqlite3"
|
DATABASES["default"]["NAME"] = "/app/data/db.sqlite3"
|
||||||
MEDIA_ROOT = "/app/media"
|
MEDIA_ROOT = "/app/media"
|
||||||
STATIC_ROOT = "/app/data/staticfiles"
|
STATIC_ROOT = "/app/data/staticfiles"
|
||||||
|
# /app/static comes from the repo-root static/ subtree of the sdist
|
||||||
|
# (see default.nix staticAssets). Added because the wheel doesn't
|
||||||
|
# ship vendored Sortable/cropper assets.
|
||||||
|
STATICFILES_DIRS = [Path("/app/static")]
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# PYTHONPATH, DJANGO_SETTINGS_MODULE, PATH, and HOME live in the image's
|
# PYTHONPATH, DJANGO_SETTINGS_MODULE, PATH, and HOME live in the image's
|
||||||
|
|
@ -195,6 +223,8 @@ pkgs.dockerTools.buildLayeredImage {
|
||||||
mkdir -p app/data app/media tmp
|
mkdir -p app/data app/media tmp
|
||||||
chmod 1777 tmp
|
chmod 1777 tmp
|
||||||
cp ${localSettings} app/local_settings.py
|
cp ${localSettings} app/local_settings.py
|
||||||
|
cp -r ${staticAssets} app/static
|
||||||
|
chmod -R u+w app/static
|
||||||
'';
|
'';
|
||||||
|
|
||||||
fakeRootCommands = ''
|
fakeRootCommands = ''
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue