Files
small-packages/luci-theme-footstrap/dev-sync.sh
T
2026-10-23 00:28:40 +08:00

125 lines
7.2 KiB
Bash
Executable File

#!/bin/sh
# Push the footstrap theme to a HARDWARE router over ssh (containers use owlab).
# ONE theme, one luci.themes entry; layout (top/sidebar) is a client axis on the
# System -> Footstrap page, not a theme entry. This registers the theme; it does NOT activate it.
set -eu
R="${1:-router2512}"
N=footstrap
D="$(cd "$(dirname "$0")" && pwd)"
# cascade.css is generated from styles/ and is not in git. --dev keeps the comments so the
# file on the router still reads like the source.
"$D"/build-css.sh "$D/htdocs/luci-static/$N/cascade.css" --dev
ssh "$R" "mkdir -p /usr/share/ucode/luci/template/themes/$N \
/www/luci-static/$N \
"
# the ONE template (+ partials/). Sidebar and top bar are the same markup, morphed by
# :root[data-layout] — there is no second theme dir to copy.
scp -q "$D"/ucode/template/themes/$N/*.ut "$R":/usr/share/ucode/luci/template/themes/$N/
ssh "$R" "mkdir -p /usr/share/ucode/luci/template/themes/$N/partials"
scp -q "$D"/ucode/template/themes/$N/partials/*.ut \
"$R":/usr/share/ucode/luci/template/themes/$N/partials/
# shared static: cascade.css, the logo (SVG + PNGs), the app icon and the manifest; no wallpapers.
scp -qr "$D"/htdocs/luci-static/$N/* "$R":/www/luci-static/$N/
# scp carries a worktree's 0600 across, and uhttpd answers 403 on a file that is not world-readable
ssh "$R" "chmod -R u=rwX,go=rX /www/luci-static/$N /usr/share/ucode/luci/template/themes/$N"
# Every resource JS goes as a TAR OF THE TREE, never by name or `scp *.js`: luci.mk copies htdocs/
# wholesale, so a file listed by hand, or one in a subdirectory, ships in the package and never
# reaches the dev router. Every path under resources/ is ours to place. --mode is GNU tar (a
# checkout's umask would otherwise set the router's modes); bsdtar on macOS refuses it, so use gtar.
tar -C "$D/htdocs/luci-static/resources" --mode=u=rwX,go=rX -cf - . |
ssh "$R" "mkdir -p /www/luci-static/resources && tar -C /www/luci-static/resources -xf -"
# stamp the git-derived version into the deployed fs-version.js (the package does the same in
# Build/Prepare) so the System -> Footstrap page shows a real version instead of "(dev)". The FILE
# NAME is part of the contract: FS_VERSION lives in fs-version.js, so moving it changes every sed.
FS_V="$(git -C "$D" describe --tags --always 2>/dev/null | sed 's/^v//')"
# if-form, not `[ -n ] && ssh`: under `set -e` a bare `&&`-list whose LAST command fails is what
# aborts, so `[ -n "$FS_V" ] && ssh …` would take the sync down whenever git describe yielded
# nothing (a copied tree without .git). An `&&` guarding an ASSIGNMENT is a different shape and is
# safe. expr refuses a tag with sed- or shell-special characters rather than interpolating it.
if [ -n "$FS_V" ] && expr "$FS_V" : '[0-9A-Za-z._-]*$' >/dev/null; then
ssh "$R" "sed -i \"s#const FS_VERSION *= *'[^']*'#const FS_VERSION = '$FS_V'#\" \
/www/luci-static/resources/fs-version.js"
fi
# The catalogue: owfeed bundles it in the release, so a dev router gets it only from this sync.
# po2lmo is a luci-base host tool, so this is best-effort: without it strings stay English. Build it
# with make -C modules/luci-base/src po2lmo (plural_formula.c is generated by contrib/lemon).
# The basename is `footstrap`, as luci.mk and owfeed.yml ship it (luci-i18n-footstrap-<lang>); the
# old `footstrap-theme.*.lmo` of an earlier dev sync is removed, or a stale catalogue could win.
if command -v po2lmo >/dev/null 2>&1; then
ssh "$R" "mkdir -p /usr/lib/lua/luci/i18n && rm -f /usr/lib/lua/luci/i18n/footstrap-theme.*.lmo"
for po in "$D"/po/*/*.po; do
[ -e "$po" ] || continue
lang="$(basename "$(dirname "$po")")"
po2lmo "$po" "/tmp/footstrap.$lang.lmo"
scp -q "/tmp/footstrap.$lang.lmo" "$R":/usr/lib/lua/luci/i18n/
rm -f "/tmp/footstrap.$lang.lmo"
done
else
echo " (po2lmo not found — skipping the translation catalogue; strings stay English)"
fi
# root/ -> / as a TREE with a GLOB of top-level dirs, never a list of names (luci.mk installs root/
# wholesale; a hand-written `usr` list missed root/etc/config/footstrap). tar, not `scp -r`: its
# merge onto an existing /usr is ambiguous. Two subtrees are excluded by behaviour, handled below:
# - etc/uci-defaults runs from /tmp; in /etc/uci-defaults the next boot would run and DELETE it.
# - etc/config holds conffiles, which the package manager keeps on upgrade and so must we: the
# live file holds the admin's "Save as default".
set --
for _d in "$D"/root/*/; do set -- "$@" "$(basename "$_d")"; done
tar -C "$D/root" --mode=u=rwX,go=rX --exclude=etc/uci-defaults --exclude=etc/config -cf - "$@" |
ssh "$R" "tar -C / -xf -"
# conffiles: install only when ABSENT, which is exactly what the package manager does with them.
for _f in "$D"/root/etc/config/*; do
[ -f "$_f" ] || continue
_b=$(basename "$_f")
scp -q "$_f" "$R":"/tmp/.fs-conf-$_b"
ssh "$R" "[ -f /etc/config/$_b ] || { mv /tmp/.fs-conf-$_b /etc/config/$_b; \
echo ' installed /etc/config/$_b (was absent)'; }; rm -f /tmp/.fs-conf-$_b"
done
# No rpcd reload here — same race as the Makefile's postinst note (Package/luci-theme-
# footstrap/postinst): this copies files onto a router where the theme registers no rpcd object,
# rpcd re-reads acl.d/*.json at LOGIN (not only on reload), and a developer's root session already
# carries read='*' write='*', so the theme's ACL group is irrelevant to it regardless.
ssh "$R" "
# Sweep every pre-consolidation name, $N-top included: a stale mediaurlbase on its media dir renders
# a theme LuCI no longer lists. rm -rf, not rm -f (it refuses directories and this block has no
# set -e); every path is a literal built from \$N.
cd /www/luci-static
rm -rf $N/$N $N-top $N-dark $N-light $N-top-dark $N-top-light
cd /usr/share/ucode/luci/template/themes
rm -rf $N/$N $N-top $N-dark $N-light $N-top-dark $N-top-light
# The overview layout must not be left in luci-mod-status's global include dir, where LuCI loads
# every *.js under any theme: it is a chrome module now. A package upgrade drops the old file, but
# dev-sync copies rather than installs, so it has to be swept by name — otherwise the dev router
# runs both copies and the grid is arranged twice.
rm -f /www/luci-static/resources/view/status/include/05_footstrap_overview_layout.js
"
# Register by running the package's own uci-defaults script — the single source of truth
# (ONE entry, luci.themes.Footstrap; layout, mode and palette are client-side toggles).
# PKG_UPGRADE=1 forces its upgrade branch, so the dev router's active theme is left alone.
scp -q "$D"/root/etc/uci-defaults/30_luci-theme-footstrap "$R":/tmp/30_luci-theme-footstrap
ssh "$R" "PKG_UPGRADE=1 sh /tmp/30_luci-theme-footstrap; rm -f /tmp/30_luci-theme-footstrap"
# ---- last: drop LuCI's menu cache and bust the browser cache ----
#
# LuCI's index cache is rebuilt from the files and the registration above. head.ut versions assets
# with `?v={{ pkgs_update_time }}`, the mtime of the PACKAGE DATABASE. An install moves it, scp does
# not, so a dev deploy keeps the URL while the bytes change and a browser may serve a stylesheet
# several deploys old. Touching the file is what an install would do.
ssh "$R" 'rm -f /tmp/luci-indexcache*;
touch /lib/apk/db/installed 2>/dev/null || touch /usr/lib/opkg/status 2>/dev/null || true'
echo "synced to $R (single Footstrap theme registered, active theme unchanged)"