Ship the manual with the Arch package, the Windows installer and the APK

The rendered manual was in the repository and nowhere else, so an
installed application still had nothing to open.

Each packager now carries docs/manual/index.html and its pictures, to
where the application will look for them: /usr/share/darkroom/manual on
Arch, manual\ beside darkroom.exe on Windows (where the models already
are, and where dr_plat::system_data_dirs points), and assets/manual in
the APK, stored rather than deflated since a GIF or PNG is already
compressed. The manual is about 27 MB, which the APK and the installer
both grow by; the pictures are 1600x1100 screenshots and short GIFs,
and against an APK that already carries 170 MB of inference runtime and
70 MB of models they are not worth re-encoding for.

The pictures are LFS objects, so each packager refuses a pointer where a
picture should be, as it already does for the models: shipped, a pointer
is a manual of broken images that nothing reports. The Android and
Windows CI legs therefore fetch docs/manual/media, which they excluded
while nothing they built read it, and the installer smoke test checks
that the page and every picture were installed.
This commit is contained in:
2026-09-24 22:33:41 -04:00
parent 10216355c1
commit d8f26fb5cd
7 changed files with 86 additions and 6 deletions
+5 -2
View File
@@ -23,6 +23,9 @@ fixtures/** filter=lfs diff=lfs merge=lfs -text
# The manual's pictures live in LFS for the same reason the models do: a
# screenshot or a GIF changes wholesale when the interface it shows changes,
# and every re-recording would otherwise stay in every clone for good. CI's
# pulls exclude the directory; nothing built or tested reads it.
# and every re-recording would otherwise stay in every clone for good. The
# desktop and benchmark legs exclude the directory, since nothing they build
# or test reads it; the Android and Windows legs fetch it, because the APK
# and the installer carry the manual (docs/manual/index.html) with its
# pictures, and their packagers refuse a pointer.
docs/manual/media/** filter=lfs diff=lfs merge=lfs -text
+14 -3
View File
@@ -100,7 +100,9 @@ jobs:
| while read -r key; do git config --local --unset-all "$key"; done || true
git config --local lfs.url \
"https://x-access-token:${LFS_TOKEN}@gitea.tourolle.paris/dtourolle/DarkRoom.git/info/lfs"
git lfs pull --exclude="fixtures/**,docs/manual/media/**"
# The manual's pictures too: the APK carries the manual, and
# assemble-apk.sh refuses a pointer where a picture should be.
git lfs pull --exclude="fixtures/**"
ls -lR models/
- name: Cache cargo
@@ -227,7 +229,9 @@ jobs:
| while read -r key; do git config --local --unset-all "$key"; done || true
git config --local lfs.url \
"https://x-access-token:${LFS_TOKEN}@gitea.tourolle.paris/dtourolle/DarkRoom.git/info/lfs"
git lfs pull --exclude="fixtures/**,docs/manual/media/**"
# The manual's pictures too: the APK carries the manual, and
# assemble-apk.sh refuses a pointer where a picture should be.
git lfs pull --exclude="fixtures/**"
ls -lR models/
- name: Cache cargo
@@ -420,7 +424,9 @@ jobs:
| while read -r key; do git config --local --unset-all "$key"; done || true
git config --local lfs.url \
"https://x-access-token:${LFS_TOKEN}@gitea.tourolle.paris/dtourolle/DarkRoom.git/info/lfs"
git lfs pull --exclude="fixtures/**,docs/manual/media/**"
# The manual's pictures too: the installer carries the manual, and
# package.sh refuses a pointer where a picture should be.
git lfs pull --exclude="fixtures/**"
ls -l models/face models/scene
- name: Cache cargo
@@ -474,6 +480,11 @@ jobs:
WANT=$(find models/face models/scene models/inpaint -maxdepth 1 -type f ! -name README.md | wc -l)
GOT=$(ls "$INST/models" | wc -l)
[ "$GOT" = "$WANT" ] || { echo "FAIL: expected $WANT model files, installed $GOT"; exit 1; }
# The manual, and every picture it shows, counted the same way.
[ -f "$INST/manual/index.html" ] || { echo "FAIL: no manual installed"; exit 1; }
WANT=$(ls docs/manual/media | wc -l)
GOT=$(ls "$INST/manual/media" | wc -l)
[ "$GOT" = "$WANT" ] || { echo "FAIL: expected $WANT manual pictures, installed $GOT"; exit 1; }
wine reg query 'HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\DarkRoom' 2>/dev/null \
| grep -q DisplayVersion || { echo "FAIL: no uninstall registry key"; exit 1; }
wine "$INST/darkroom.exe" --version 2>/dev/null | grep -q '^darkroom-desktop ' \
+23
View File
@@ -332,6 +332,29 @@ else
echo " assets: no models found (face indexing and the scene tab will be off on the device)"
fi
# The manual: the rendered page and its pictures, read in place by
# ManualActivity's WebView as file:///android_asset/manual/index.html. Not
# unpacked like the models: a WebView reads an asset straight out of the APK,
# and relative links to media/ resolve inside the same asset tree, so the page
# costs no first-launch copy and no second copy on /data.
#
# About 27 MB, stored below like the models — a GIF or PNG is already
# compressed, and deflating it again buys nothing. The pictures are LFS
# objects, and unlike a missing model a pointer would not fail loudly: it
# ships as a manual full of broken images. So it stops the build here.
rm -rf "${OUT}/staging/assets/manual"
mkdir -p "${OUT}/staging/assets/manual/media"
cp "${REPO}/docs/manual/index.html" "${OUT}/staging/assets/manual/"
for f in "${REPO}/docs/manual/media"/*; do
if head -c 40 "${f}" | grep -q '^version https://git-lfs'; then
echo "error: $(basename "${f}") is an LFS pointer, not a picture." >&2
echo " run: git lfs pull --include='docs/manual/media/**'" >&2
exit 1
fi
cp "${f}" "${OUT}/staging/assets/manual/media/"
done
echo " manual: index.html and $(ls "${OUT}/staging/assets/manual/media" | wc -l) picture(s), $(du -sh "${OUT}/staging/assets/manual" | cut -f1)"
# -0 "" stores the .so without compression so Android can mmap it directly
# (extractNativeLibs=false territory); for a 37 MB library that also keeps
# install times sane.
+17
View File
@@ -69,6 +69,23 @@ for dir in face scene inpaint; do
done
echo "==> staged $(ls "${STAGE}/models" | wc -l) model file(s)"
# The manual: the rendered page and its pictures, beside the executable where
# dr_ui::manual looks on Windows (dr_plat::system_data_dirs is the exe's own
# directory there). The pictures are LFS objects; a pointer is ~130 bytes of
# text that every browser draws as a broken image, so refuse it here rather
# than ship a manual with no pictures in it.
mkdir -p "${STAGE}/manual/media"
cp "${REPO}/docs/manual/index.html" "${STAGE}/manual/"
for f in "${REPO}/docs/manual/media"/*; do
if head -c 40 "${f}" | grep -q '^version https://git-lfs'; then
echo "error: $(basename "${f}") is an LFS pointer, not a picture." >&2
echo " run: git lfs pull --include='docs/manual/media/**'" >&2
exit 1
fi
cp "${f}" "${STAGE}/manual/media/"
done
echo "==> staged the manual and $(ls "${STAGE}/manual/media" | wc -l) picture(s)"
# One installer in the output directory, the one just built. The directory
# is cached between CI runs, so after a version bump a glob over it would find
# two and the smoke test would hand Wine both names as one path.
+2
View File
@@ -285,6 +285,8 @@ $LOCALAPPDATA\Programs\DarkRoom\
scrfd_500m_640.int8.onnx scrfd_2.5g_640.int8.onnx scrfd_10g_640.int8.onnx
yolo26s-sem-ade20k.onnx yolo26s-sem-ade20k.classes.json categories.txt
migan-512.onnx
manual\
index.html media\ (the rendered manual and its pictures)
LICENSE
uninstall.exe
```
+15
View File
@@ -59,6 +59,21 @@ package() {
install -Dm644 "README.md" "${pkgdir}/usr/share/doc/${pkgname}/README.md"
# The manual: the rendered page and its pictures, where the app's Help
# opens it (dr_ui::manual, through dr_plat::system_data_dirs). Offline by
# design — the help sheet's "See it" links land here, on a machine that
# may have no network. The pictures are in LFS like the models, and a
# pointer shipped in their place is a manual of broken images, so the
# same refusal applies.
install -Dm644 "docs/manual/index.html" "${pkgdir}/usr/share/darkroom/manual/index.html"
for _f in docs/manual/media/*; do
if head -c 40 "${_f}" | grep -q '^version https://git-lfs'; then
echo "error: $(basename "${_f}") is an LFS pointer, not a picture — run: git lfs pull" >&2
return 1
fi
install -Dm644 "${_f}" "${pkgdir}/usr/share/darkroom/manual/media/$(basename "${_f}")"
done
# The face models, into the last directory the app searches. A pair the
# user placed in their own data directory outranks these, so installing
# them cannot override a deliberate choice of weights.
+10 -1
View File
@@ -6,7 +6,8 @@
;
; makensis -DVERSION=0.12.0 -DSTAGE=/path/to/staging -DOUT=/path/to/setup.exe darkroom.nsi
;
; STAGE holds exactly what §5.2 installs: darkroom.exe, models\ and LICENSE.
; STAGE holds exactly what §5.2 installs: darkroom.exe, models\, manual\ and
; LICENSE.
; package.sh assembles it and applies the LFS-pointer guard before this runs.
; A 64-bit installer, not NSIS's default 32-bit stub. The application is
@@ -76,6 +77,13 @@ Section "DarkRoom" SecMain
SetOutPath "$INSTDIR\models"
File /r "${STAGE}\models\*"
; The manual and its pictures, opened by Help and by the help sheet's
; "See it" links (dr_ui::manual). Beside the executable for the same reason
; the models are: it is the one directory the application can find without
; asking the registry.
SetOutPath "$INSTDIR\manual"
File /r "${STAGE}\manual\*"
WriteUninstaller "$INSTDIR\uninstall.exe"
; Add/Remove Programs. HKCU, to match the per-user install.
@@ -114,6 +122,7 @@ Section "Uninstall"
Delete "$INSTDIR\LICENSE"
Delete "$INSTDIR\uninstall.exe"
RMDir /r "$INSTDIR\models"
RMDir /r "$INSTDIR\manual"
RMDir "$INSTDIR"
Delete "$SMPROGRAMS\${NAME}\${NAME}.lnk"