refactor(webinstaller): extract inline payload constants to furtka/assets/
Slice 1a of the self-update story. Every HTML/CSS/shell-script/systemd-
unit payload that used to live as a triple-quoted string constant inside
webinstaller/app.py now lives as a real file under furtka/assets/:
furtka/assets/Caddyfile
furtka/assets/VERSION (new — matches pyproject.toml)
furtka/assets/www/{index.html, settings/index.html, style.css, status.json}
furtka/assets/bin/{furtka-status, furtka-welcome}
furtka/assets/systemd/furtka-{api,reconcile,status,welcome}.service
furtka/assets/systemd/furtka-status.timer
The installer now pulls each file from disk via _read_asset(). Byte-for-
byte identical output at install time — a fresh-ISO install should land
the same files in the same places with the same contents, verified by
tests/test_webinstaller_assets.py which reconstructs each base64 blob
and asserts equality against the on-disk asset.
iso/build.sh also copies furtka/assets/ next to the webinstaller source
at /opt/furtka/assets on the live ISO so _resolve_assets_dir() finds
them with a "next to me" lookup. In dev the same function walks two
levels up to the repo copy, so pytest works without any env vars.
furtka-status.sh drops the /etc/furtka/version TODO — it now reads
/opt/furtka/VERSION directly, which Slice 1b will upgrade to
/opt/furtka/current/VERSION once the symlink layout lands.
_FURTKA_WRAPPER_SH (the 5-line /usr/local/bin/furtka shim) stays inline;
it's tiny and not asset-shaped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:08:53 +02:00
|
|
|
#!/bin/bash
|
|
|
|
|
# Regenerates /etc/issue on the installed system so the console tells the
|
2026-08-25 15:18:39 +02:00
|
|
|
# user which URL to open. Mirrors the live-ISO furtka-update-issue pattern:
|
|
|
|
|
# runs at boot and every few seconds from furtka-welcome.timer, so the IP
|
|
|
|
|
# fallback line is always on screen — with the address once DHCP handed one
|
|
|
|
|
# out, or with a "no IP yet" hint before that / after the cable is pulled.
|
|
|
|
|
# Only rewrites + nudges agetty when the text changed (no prompt flicker).
|
|
|
|
|
set -u
|
refactor(webinstaller): extract inline payload constants to furtka/assets/
Slice 1a of the self-update story. Every HTML/CSS/shell-script/systemd-
unit payload that used to live as a triple-quoted string constant inside
webinstaller/app.py now lives as a real file under furtka/assets/:
furtka/assets/Caddyfile
furtka/assets/VERSION (new — matches pyproject.toml)
furtka/assets/www/{index.html, settings/index.html, style.css, status.json}
furtka/assets/bin/{furtka-status, furtka-welcome}
furtka/assets/systemd/furtka-{api,reconcile,status,welcome}.service
furtka/assets/systemd/furtka-status.timer
The installer now pulls each file from disk via _read_asset(). Byte-for-
byte identical output at install time — a fresh-ISO install should land
the same files in the same places with the same contents, verified by
tests/test_webinstaller_assets.py which reconstructs each base64 blob
and asserts equality against the on-disk asset.
iso/build.sh also copies furtka/assets/ next to the webinstaller source
at /opt/furtka/assets on the live ISO so _resolve_assets_dir() finds
them with a "next to me" lookup. In dev the same function walks two
levels up to the repo copy, so pytest works without any env vars.
furtka-status.sh drops the /etc/furtka/version TODO — it now reads
/opt/furtka/VERSION directly, which Slice 1b will upgrade to
/opt/furtka/current/VERSION once the symlink layout lands.
_FURTKA_WRAPPER_SH (the 5-line /usr/local/bin/furtka shim) stays inline;
it's tiny and not asset-shaped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:08:53 +02:00
|
|
|
|
2026-08-25 15:18:39 +02:00
|
|
|
issue=/etc/issue
|
refactor(webinstaller): extract inline payload constants to furtka/assets/
Slice 1a of the self-update story. Every HTML/CSS/shell-script/systemd-
unit payload that used to live as a triple-quoted string constant inside
webinstaller/app.py now lives as a real file under furtka/assets/:
furtka/assets/Caddyfile
furtka/assets/VERSION (new — matches pyproject.toml)
furtka/assets/www/{index.html, settings/index.html, style.css, status.json}
furtka/assets/bin/{furtka-status, furtka-welcome}
furtka/assets/systemd/furtka-{api,reconcile,status,welcome}.service
furtka/assets/systemd/furtka-status.timer
The installer now pulls each file from disk via _read_asset(). Byte-for-
byte identical output at install time — a fresh-ISO install should land
the same files in the same places with the same contents, verified by
tests/test_webinstaller_assets.py which reconstructs each base64 blob
and asserts equality against the on-disk asset.
iso/build.sh also copies furtka/assets/ next to the webinstaller source
at /opt/furtka/assets on the live ISO so _resolve_assets_dir() finds
them with a "next to me" lookup. In dev the same function walks two
levels up to the repo copy, so pytest works without any env vars.
furtka-status.sh drops the /etc/furtka/version TODO — it now reads
/opt/furtka/VERSION directly, which Slice 1b will upgrade to
/opt/furtka/current/VERSION once the symlink layout lands.
_FURTKA_WRAPPER_SH (the 5-line /usr/local/bin/furtka shim) stays inline;
it's tiny and not asset-shaped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:08:53 +02:00
|
|
|
|
2026-08-25 15:18:39 +02:00
|
|
|
hostname=$(cat /etc/hostname 2>/dev/null || hostname)
|
|
|
|
|
mapfile -t ips < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
|
|
|
|
|
|
|
|
|
|
new=$(
|
refactor(webinstaller): extract inline payload constants to furtka/assets/
Slice 1a of the self-update story. Every HTML/CSS/shell-script/systemd-
unit payload that used to live as a triple-quoted string constant inside
webinstaller/app.py now lives as a real file under furtka/assets/:
furtka/assets/Caddyfile
furtka/assets/VERSION (new — matches pyproject.toml)
furtka/assets/www/{index.html, settings/index.html, style.css, status.json}
furtka/assets/bin/{furtka-status, furtka-welcome}
furtka/assets/systemd/furtka-{api,reconcile,status,welcome}.service
furtka/assets/systemd/furtka-status.timer
The installer now pulls each file from disk via _read_asset(). Byte-for-
byte identical output at install time — a fresh-ISO install should land
the same files in the same places with the same contents, verified by
tests/test_webinstaller_assets.py which reconstructs each base64 blob
and asserts equality against the on-disk asset.
iso/build.sh also copies furtka/assets/ next to the webinstaller source
at /opt/furtka/assets on the live ISO so _resolve_assets_dir() finds
them with a "next to me" lookup. In dev the same function walks two
levels up to the repo copy, so pytest works without any env vars.
furtka-status.sh drops the /etc/furtka/version TODO — it now reads
/opt/furtka/VERSION directly, which Slice 1b will upgrade to
/opt/furtka/current/VERSION once the symlink layout lands.
_FURTKA_WRAPPER_SH (the 5-line /usr/local/bin/furtka shim) stays inline;
it's tiny and not asset-shaped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:08:53 +02:00
|
|
|
echo
|
|
|
|
|
echo " Furtka is ready."
|
|
|
|
|
echo
|
|
|
|
|
echo " Open in a browser on another device on your network:"
|
|
|
|
|
echo
|
|
|
|
|
echo " http://${hostname}.local (easy — try this first)"
|
2026-08-25 15:18:39 +02:00
|
|
|
if ((${#ips[@]})); then
|
|
|
|
|
for ip in "${ips[@]}"; do
|
|
|
|
|
echo " http://${ip} (fallback if the first doesn't work)"
|
|
|
|
|
done
|
|
|
|
|
else
|
|
|
|
|
echo " (fallback: no IP address yet — waiting for the network."
|
|
|
|
|
echo " Check the cable / DHCP. This line updates by itself.)"
|
refactor(webinstaller): extract inline payload constants to furtka/assets/
Slice 1a of the self-update story. Every HTML/CSS/shell-script/systemd-
unit payload that used to live as a triple-quoted string constant inside
webinstaller/app.py now lives as a real file under furtka/assets/:
furtka/assets/Caddyfile
furtka/assets/VERSION (new — matches pyproject.toml)
furtka/assets/www/{index.html, settings/index.html, style.css, status.json}
furtka/assets/bin/{furtka-status, furtka-welcome}
furtka/assets/systemd/furtka-{api,reconcile,status,welcome}.service
furtka/assets/systemd/furtka-status.timer
The installer now pulls each file from disk via _read_asset(). Byte-for-
byte identical output at install time — a fresh-ISO install should land
the same files in the same places with the same contents, verified by
tests/test_webinstaller_assets.py which reconstructs each base64 blob
and asserts equality against the on-disk asset.
iso/build.sh also copies furtka/assets/ next to the webinstaller source
at /opt/furtka/assets on the live ISO so _resolve_assets_dir() finds
them with a "next to me" lookup. In dev the same function walks two
levels up to the repo copy, so pytest works without any env vars.
furtka-status.sh drops the /etc/furtka/version TODO — it now reads
/opt/furtka/VERSION directly, which Slice 1b will upgrade to
/opt/furtka/current/VERSION once the symlink layout lands.
_FURTKA_WRAPPER_SH (the 5-line /usr/local/bin/furtka shim) stays inline;
it's tiny and not asset-shaped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:08:53 +02:00
|
|
|
fi
|
|
|
|
|
echo
|
2026-08-25 15:18:39 +02:00
|
|
|
)
|
refactor(webinstaller): extract inline payload constants to furtka/assets/
Slice 1a of the self-update story. Every HTML/CSS/shell-script/systemd-
unit payload that used to live as a triple-quoted string constant inside
webinstaller/app.py now lives as a real file under furtka/assets/:
furtka/assets/Caddyfile
furtka/assets/VERSION (new — matches pyproject.toml)
furtka/assets/www/{index.html, settings/index.html, style.css, status.json}
furtka/assets/bin/{furtka-status, furtka-welcome}
furtka/assets/systemd/furtka-{api,reconcile,status,welcome}.service
furtka/assets/systemd/furtka-status.timer
The installer now pulls each file from disk via _read_asset(). Byte-for-
byte identical output at install time — a fresh-ISO install should land
the same files in the same places with the same contents, verified by
tests/test_webinstaller_assets.py which reconstructs each base64 blob
and asserts equality against the on-disk asset.
iso/build.sh also copies furtka/assets/ next to the webinstaller source
at /opt/furtka/assets on the live ISO so _resolve_assets_dir() finds
them with a "next to me" lookup. In dev the same function walks two
levels up to the repo copy, so pytest works without any env vars.
furtka-status.sh drops the /etc/furtka/version TODO — it now reads
/opt/furtka/VERSION directly, which Slice 1b will upgrade to
/opt/furtka/current/VERSION once the symlink layout lands.
_FURTKA_WRAPPER_SH (the 5-line /usr/local/bin/furtka shim) stays inline;
it's tiny and not asset-shaped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:08:53 +02:00
|
|
|
|
2026-08-25 15:18:39 +02:00
|
|
|
if [ "$new" != "$(cat "$issue" 2>/dev/null)" ]; then
|
|
|
|
|
printf '%s\n' "$new" > "$issue"
|
|
|
|
|
agetty --reload 2>/dev/null || true
|
|
|
|
|
fi
|