From 38daa3b0c30972ca984bae6d786ac885ea42e587 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 11 May 2026 17:02:31 -0700 Subject: [PATCH 1/5] C1: bump shower to v1.1.0 (probe FOD hash) Co-Authored-By: Claude Opus 4.7 (1M context) --- containers/shower/default.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/containers/shower/default.nix b/containers/shower/default.nix index d9863e1..32419e2 100644 --- a/containers/shower/default.nix +++ b/containers/shower/default.nix @@ -21,7 +21,7 @@ { pkgs ? import { } }: let - version = "1.0.2"; + version = "1.1.0"; python = pkgs.python314; @@ -39,7 +39,7 @@ let 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="; + hash = "sha256-5dp+0u4metOIC6s6/nPlT4cdpFBCV6S3+Z/3RO0sX5U="; }; staticAssets = pkgs.runCommand "shower-static-assets-${version}" { } '' @@ -129,7 +129,7 @@ let outputHashAlgo = "sha256"; # Pinned dep closure — reproducible until version bumps. To recompute, # set to pkgs.lib.fakeHash and read the failure. - outputHash = "sha256-tSTH/HaDY7M0qxlauBTM+JekZAgF++K2lGP3PLvym/o="; + outputHash = pkgs.lib.fakeHash; dontFixup = true; }; -- 2.50.1 (Apple Git-155) From e5341002544d1a7dce5c4b9527e84b71a20e77e1 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 11 May 2026 17:14:35 -0700 Subject: [PATCH 2/5] =?UTF-8?q?C1:=20shower=20default.nix=20=E2=80=94=20fe?= =?UTF-8?q?tchurl=20wheel=20directly=20(probe=20FOD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forgejo's PyPI simple index returns absolute file URLs hardcoded to its public ROOT_URL (forge.eblu.me). The Fly edge blocks /api/packages/* per PR #2d38418e, so pip-install through the index 403s the wheel download even when reached from the tailnet. Mirror what we already do for the sdist: pull the wheel via fetchurl from forge.ops.eblu.me and hand it to pip as a local path. The forge --extra-index-url is no longer needed. Also bumps service-versions.yaml current-version to v1.1.0 to satisfy container-version-check (the kustomization newTag follows once the nix build pins its FOD hash). Co-Authored-By: Claude Opus 4.7 (1M context) --- containers/shower/default.nix | 27 ++++++++++++++++++++------- service-versions.yaml | 4 ++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/containers/shower/default.nix b/containers/shower/default.nix index 32419e2..6e97d6b 100644 --- a/containers/shower/default.nix +++ b/containers/shower/default.nix @@ -1,11 +1,15 @@ # Nix-built shower app container — Adelaide / Heidi / Addie baby shower. # # The app is published as a wheel to the Forgejo PyPI index at -# https://forge.eblu.me/api/packages/eblume/pypi/. The wheel + its -# transitive Python deps are baked in at build time via a fixed-output -# derivation that runs `pip install --target` against forge PyPI (proxied -# through pypi.ops.eblu.me for upstream packages). Build runs on the -# nix-container-builder runner (ringtail, amd64) so the image is native. +# https://forge.ops.eblu.me/api/packages/eblume/pypi/ (tailnet-only — the +# public forge.eblu.me /api/packages/* surface is blocked at the Fly edge). +# We can't point pip at Forgejo's simple index even from the tailnet, +# because Forgejo's index returns absolute file URLs hardcoded to its +# public ROOT_URL (forge.eblu.me), which then 403s. So both the wheel and +# the sdist are pulled by direct `fetchurl` against forge.ops.eblu.me, and +# the wheel is then handed to `pip install` as a local path; transitive +# deps come from pypi.ops.eblu.me. Build runs on the nix-container-builder +# runner (ringtail, amd64) so the image is native. # # Going through pip-install-target rather than nixpkgs Python packages # sidesteps two issues we hit going through `python.pkgs.buildPythonPackage`: @@ -42,6 +46,16 @@ let hash = "sha256-5dp+0u4metOIC6s6/nPlT4cdpFBCV6S3+Z/3RO0sX5U="; }; + # Wheel pulled from forge.ops.eblu.me (tailnet) for the same reason the + # sdist is: Forgejo's PyPI simple index would return forge.eblu.me URLs + # that the Fly edge 403s on /api/packages/*. We hand this path to pip + # below so it never touches the forge index at all. + showerWheel = pkgs.fetchurl { + name = "adelaide_baby_shower_app-${version}-py3-none-any.whl"; + url = "https://forge.ops.eblu.me/api/packages/eblume/pypi/files/adelaide-baby-shower-app/${version}/adelaide_baby_shower_app-${version}-py3-none-any.whl"; + hash = "sha256-7orFbycON9dQxEIb6q45Xx2rFlEZ8xXSrC2tnrO5uug="; + }; + 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 @@ -71,8 +85,7 @@ let "$TMPDIR/venv/bin/pip" install \ --no-cache-dir \ --index-url=https://pypi.ops.eblu.me/root/pypi/+simple/ \ - --extra-index-url=https://forge.ops.eblu.me/api/packages/eblume/pypi/simple/ \ - "adelaide-baby-shower-app==${version}" \ + ${showerWheel} \ gunicorn runHook postBuild diff --git a/service-versions.yaml b/service-versions.yaml index 56000df..63bc5df 100644 --- a/service-versions.yaml +++ b/service-versions.yaml @@ -46,8 +46,8 @@ services: - name: shower type: argocd - last-reviewed: 2026-05-10 - current-version: "1.0.2" + last-reviewed: 2026-05-11 + current-version: "1.1.0" upstream-source: https://forge.eblu.me/eblume/adelaide-baby-shower-app notes: | Django app for Adelaide / Heidi / Addie's baby shower. Wheel -- 2.50.1 (Apple Git-155) From a156ea7687ad131ce5b60a2801e337fd30e57681 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 11 May 2026 17:17:02 -0700 Subject: [PATCH 3/5] =?UTF-8?q?C1:=20shower=20=E2=80=94=20copy=20wheel=20t?= =?UTF-8?q?o=20clean=20filename=20before=20pip=20install?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nix store paths embed a 32-char hash prefix, which pip rejects as "Invalid wheel filename (invalid version)". Copy the wheel into TMPDIR under its bare name first, then install from there. Co-Authored-By: Claude Opus 4.7 (1M context) --- containers/shower/default.nix | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/containers/shower/default.nix b/containers/shower/default.nix index 6e97d6b..4d055bf 100644 --- a/containers/shower/default.nix +++ b/containers/shower/default.nix @@ -82,10 +82,16 @@ let ${python}/bin/python -m venv "$TMPDIR/venv" "$TMPDIR/venv/bin/pip" install --upgrade pip + + # Nix store paths embed a 32-char hash prefix, which pip's wheel + # filename parser rejects ("Invalid wheel filename"). Copy to a + # clean filename in TMPDIR before installing. + cp ${showerWheel} "$TMPDIR/${showerWheel.name}" + "$TMPDIR/venv/bin/pip" install \ --no-cache-dir \ --index-url=https://pypi.ops.eblu.me/root/pypi/+simple/ \ - ${showerWheel} \ + "$TMPDIR/${showerWheel.name}" \ gunicorn runHook postBuild -- 2.50.1 (Apple Git-155) From 444ff91b61aec8d02fa7efc69c7daf8d04deb25a Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 11 May 2026 17:22:20 -0700 Subject: [PATCH 4/5] C1: pin shower v1.1.0 FOD outputHash from run 547 Co-Authored-By: Claude Opus 4.7 (1M context) --- containers/shower/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/containers/shower/default.nix b/containers/shower/default.nix index 4d055bf..e2d369d 100644 --- a/containers/shower/default.nix +++ b/containers/shower/default.nix @@ -148,7 +148,7 @@ let outputHashAlgo = "sha256"; # Pinned dep closure — reproducible until version bumps. To recompute, # set to pkgs.lib.fakeHash and read the failure. - outputHash = pkgs.lib.fakeHash; + outputHash = "sha256-kTNOswobtkgyQmmqbQM8XO4vvaGg57nCuuZGbNXb0NM="; dontFixup = true; }; -- 2.50.1 (Apple Git-155) From 66cdd01999721b1843d708b00343fc2677ec1d51 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 11 May 2026 18:35:26 -0700 Subject: [PATCH 5/5] C1: deploy shower v1.1.0 to ringtail (kustomization + changelog) Co-Authored-By: Claude Opus 4.7 (1M context) --- argocd/manifests/shower/kustomization.yaml | 2 +- docs/changelog.d/shower-v1.1.0.feature.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 docs/changelog.d/shower-v1.1.0.feature.md diff --git a/argocd/manifests/shower/kustomization.yaml b/argocd/manifests/shower/kustomization.yaml index d2ce83c..6fe641f 100644 --- a/argocd/manifests/shower/kustomization.yaml +++ b/argocd/manifests/shower/kustomization.yaml @@ -14,4 +14,4 @@ resources: images: - name: registry.ops.eblu.me/blumeops/shower - newTag: v1.0.2-292d354-nix + newTag: v1.1.0-444ff91-nix diff --git a/docs/changelog.d/shower-v1.1.0.feature.md b/docs/changelog.d/shower-v1.1.0.feature.md new file mode 100644 index 0000000..d2c3400 --- /dev/null +++ b/docs/changelog.d/shower-v1.1.0.feature.md @@ -0,0 +1,15 @@ +Deploy adelaide-baby-shower-app v1.1.0 to ringtail k3s. Replaces the +boolean lock with a four-phase `ShowerState` (`pre_event` → `party` → +`prizes_locked` → `event_locked`), adds an append-only "guest memories" +panel where guests can leave photos and comments for the baby, and +polishes the admin and QR views. Three Django migrations +(`0009_shower_phase`, `0010_guest_memories`, `0011_book_description`) +run automatically in the entrypoint against the SQLite PV. No config +or env-var changes. + +Container build also gains a Forgejo-PyPI workaround: Forgejo's simple +index returns absolute file URLs hardcoded to the public ROOT_URL +(`forge.eblu.me`), which the Fly edge 403s on `/api/packages/*`. The +wheel and sdist are now both pulled via direct `fetchurl` against +`forge.ops.eblu.me` (tailnet-only) and the wheel is handed to pip as +a local path. -- 2.50.1 (Apple Git-155)