Files
op-packages/luci-theme-footstrap/Makefile
T

222 lines
13 KiB
Makefile
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#
# Copyright (C) 2026
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-theme-footstrap
# luci.mk keys the Build/Prepare hook name on LUCI_NAME, which defaults to the checkout's
# directory name — a differently-named checkout symlinked into the feed would silently skip
# the CSS build. Pin it.
LUCI_NAME:=luci-theme-footstrap
# Version is git-derived; never hardcode PKG_VERSION. CI injects FOOTSTRAP_VERSION from the
# tag (an SDK build has no .git, so it cannot derive one).
FOOTSTRAP_VERSION?=
ifneq ($(FOOTSTRAP_VERSION),)
PKG_VERSION:=$(FOOTSTRAP_VERSION)
PKG_RELEASE:=9
endif
LUCI_TITLE:=Footstrap Theme
# +luci-base is the WHOLE list. The self-updater used to hard-require `curl`, which is NOT in
# OpenWrt's default package set (the base image ships uclient-fetch), so the update button
# failed on a stock router; it falls back to uclient-fetch rather than pull curl onto an 8 MB
# device for one API call. jsonfilter and sha256sum ARE in the base image. Do not add a
# runtime dep for a convenience tool — fall back instead.
LUCI_DEPENDS:=+luci-base
LUCI_PKGARCH:=all
# Else luci.mk defaults these to LuCI's own, and the package claims MAINTAINER "OpenWrt LuCI
# community" / URL openwrt/luci.
LUCI_MAINTAINER:=VizzleTF <vizzletf47@gmail.com>
LUCI_URL:=https://github.com/VizzleTF/luci-theme-footstrap
# CSS: ship verbatim. luci.mk would run csstidy over it, which is old enough to mangle
# :has()/color-mix()/nested calc() and break the layout. build-css.sh minifies instead.
LUCI_MINIFY_CSS:=0
#
# JS: TWO minify paths, chosen by who is building.
#
# CI (the released packages) pre-minifies with terser BEFORE the SDK build (tools/minify-js.mjs:
# mangles top-level identifiers too, which jsmin cannot — ~41 KB against jsmin's ~57 KB, 27%)
# and signals it with FOOTSTRAP_PREMIN=1. jsmin must then stay AWAY from the already-minified
# output: terser legitimately emits `return/^v/.test(s)`, the exact one-character-lookback trap
# below, so LUCI_MINIFY_JS goes to 0 for that build.
#
# Everyone else (an SDK user, the buildbot — no node) builds the untouched source and KEEPS
# luci.mk's default jsmin (LUCI_MINIFY_JS=1): ~105 KB of this tree's JS is comments, and uhttpd
# serves /www with NO compression, so those are wire AND flash bytes either way.
#
# The catch on the jsmin path, and why the gates exist: jsmin decides `/` = regex-or-division
# from a ONE-character lookback, and `n` (of `return`) and `>` (of `=>`) are not on its
# allow-list — so `return /re/` makes it eat the rest of the file AND EXIT 0
# (openwrt/luci#8299). eslint's `wrap-regex` bans the shape in the SOURCE; tools/jsmin-verify.mjs
# proves jsmin's output is TOKEN-IDENTICAL to it. Both keep guarding the non-CI path.
FOOTSTRAP_PREMIN?=
ifneq ($(FOOTSTRAP_PREMIN),)
LUCI_MINIFY_JS:=0
endif
# Apache-2.0, and not a free choice: styles/base began as a fork of luci-theme-bootstrap's
# cascade.css, the ucode templates derive from LuCI's own, and a few JS helpers are verbatim
# copies. The notices travel with it.
#
# THE OFL-1.1 HALF IS GONE WITH THE WEBFONTS. The theme carried subsetted Manrope and JetBrains
# Mono and had to ship the OFL text beside them (§2 requires the notice and licence with every
# copy of the Font Software). It ships no Font Software now, so declaring OFL would be a false
# statement about the package's contents.
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=LICENSE
# /etc/config/footstrap is SHIPPED as an empty stub and WRITTEN AT RUNTIME: Appearance -> "Save as
# default" has rpcd uci-set the router-wide axes into that very file (fs-prefs.js saveAsDefault()).
# Without this define the package manager owns it as an ordinary file and REPLACES it on upgrade —
# so the admin's saved defaults were wiped by the theme's own one-click Update, silently, reported
# as success. Measured on the dev router: eight options in the live file, package-owned, no
# .conffiles entry beside base-files'/dnsmasq's. OpenWrt honours this for BOTH formats
# (include/package-pack.mk: KEEP_$(1) -> apk .conffiles, ipk CONTROL/conffiles), so one define
# covers 24.10's opkg and 25.12's apk alike. Any future root/etc/config/* must be listed here too.
define Package/luci-theme-footstrap/conffiles
/etc/config/footstrap
endef
# RELOAD, NOT RESTART: rpcd holds sessions in memory, so `restart` logs out every LuCI user —
# including the admin who just clicked Update. `reload` sends SIGHUP, which re-reads
# /usr/share/rpcd/acl.d/*, and that ACL refresh is the only thing this package needs from rpcd.
# Verified on a live router: a session survives `reload` and dies across `restart`; deleting our
# acl.d file + `reload` flips `session access` for the self-update script from true to false.
define Package/luci-theme-footstrap/postinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
# uci-defaults registers the theme; refresh rpcd + drop caches so LuCI sees it without a
# reboot. Calling it ourselves is belt-and-braces: OpenWrt's default_postinst also runs
# (then deletes) every /etc/uci-defaults/* we ship, so it executes twice per install. It is
# idempotent, and its fresh-vs-upgrade marker is written at the end of the first pass, so
# that pass still sees a fresh install.
[ -f /etc/uci-defaults/30_luci-theme-footstrap ] && \
sh /etc/uci-defaults/30_luci-theme-footstrap >/dev/null 2>&1 || true
rm -f /tmp/luci-indexcache* /tmp/luci-modulecache/* >/dev/null 2>&1 || true
/etc/init.d/rpcd reload >/dev/null 2>&1 || true
}
exit 0
endef
define Package/luci-theme-footstrap/postrm
#!/bin/sh
# opkg runs the OLD package's postrm with arg "upgrade" during a version upgrade (proven on 24.10:
# `prerm upgrade`, then `postrm upgrade`, then the new `postinst configure`) and "remove" on a real
# removal. On upgrade this script MUST change nothing: reverting mediaurlbase to bootstrap, wiping
# the theme registration and deleting the .installed marker here is what flipped every updating
# 24.10 user back to bootstrap — and it defeated uci-defaults' own upgrade guard, because with the
# marker gone uci-defaults treated the run as "fresh", yet postrm had already set mediaurlbase to
# bootstrap, which no "fresh" branch re-activates. apk never runs this on upgrade (it uses the new
# package's pre/post-upgrade), so guarding on the arg is correct for both managers; real removal
# passes "remove" (opkg) or no arg and proceeds below.
case "$$1" in *upgrade*) exit 0 ;; esac
[ -n "$${IPKG_INSTROOT}" ] || {
# Every theme name this package has ever registered. postrm runs when uci-defaults is
# already deleted, so it cannot source the list: it is duplicated by necessity and
# mirror-pinned so it cannot drift. Only the assignment is byte-identical — the `$$` is
# make's escaping.
# @mirror theme/legacy-names
LEGACY_THEMES="Footstrap FootstrapDark FootstrapLight FootstrapTop FootstrapTopDark FootstrapTopLight FootstrapSidebar FootstrapOnTop"
# @endmirror
for t in $$LEGACY_THEMES; do
uci -q delete "luci.themes.$$t"
done
# The router's DEFAULT layout, set by uci-defaults when migrating an old top-nav install.
# It means nothing to any other theme, and left behind it would silently re-apply the top
# bar on a later reinstall.
uci -q delete luci.main.footstrap_layout
# Don't leave the active theme pointing at the media dir we just removed. The same TWO-part
# check uci-defaults uses: a theme needs its media dir AND its ucode template to render, so
# a one-part check could hand the UI to a half-removed bootstrap — the blank page this
# branch exists to avoid.
case "$$(uci -q get luci.main.mediaurlbase)" in
/luci-static/footstrap*)
[ -d /www/luci-static/bootstrap ] && \
[ -f /usr/share/ucode/luci/template/themes/bootstrap/header.ut ] && \
uci set luci.main.mediaurlbase=/luci-static/bootstrap ;;
esac
uci commit luci
# The "installed before" marker uci-defaults writes to tell fresh from upgrade. Removing it
# makes uninstall-then-reinstall count as fresh again.
rm -rf /usr/share/luci-theme-footstrap >/dev/null 2>&1 || true
# The admin-uploaded login background (kept out of the package on purpose, so it survives an
# upgrade). A real removal is the one time it should go — this branch never runs on upgrade.
rm -rf /etc/footstrap >/dev/null 2>&1 || true
rm -f /tmp/luci-indexcache* /tmp/luci-modulecache/* >/dev/null 2>&1 || true
/etc/init.d/rpcd reload >/dev/null 2>&1 || true # see the postinst note: reload keeps sessions
}
endef
# Runs right after luci.mk copies luasrc/ucode/htdocs/root/src into PKG_BUILD_DIR, so it
# rewrites the build copy, never the source tree.
#
# 1. cascade.css is generated, not committed — and styles/ is not in luci.mk's copy list, so
# build-css.sh reads it from $(CURDIR) and writes into the build tree. cat/awk only, no host
# toolchain: that is why it runs on an OpenWrt buildbot.
# 2. Stamp the git-derived version into the theme JS (fs-version.js — the string the Appearance tab
# shows). Skipped when PKG_VERSION is empty, leaving '0.0.0-dev'.
#
# THE CATALOGUE IS luci.mk's BUSINESS, and nothing here compiles it. `po/` is the directory
# LUCI_LANGUAGES globs, so luci.mk emits one `luci-i18n-footstrap-<lang>` package per language the
# way it does for every luci-app — which is what a package IN THE LUCI TREE has to look like, and
# what Weblate can see (it translates `po/`, and only `po/`).
#
# It was `i18n/` here for a while, with a po2lmo loop in this hook bundling the catalogue into the
# theme package, because a multi-package release was mis-picked by a self-updater that resolved the
# theme by name and took `head -1` (issue #6). That updater is retired and the release is built by
# owfeed, which packages this theme as exactly one artifact per format regardless of what luci.mk
# would do — so the constraint that bought the rename is gone, and the cost of keeping it (a
# catalogue invisible to the project's own translation platform) is not.
define Build/Prepare/luci-theme-footstrap
# luci.mk's Build/Prepare copies only src/ luasrc/ htdocs/ root/ ucode/ into
# $(PKG_BUILD_DIR), and PKG_LICENSE_FILES resolves against THAT — so the Apache text has to
# be put there by hand or the declaration points at nothing.
$(CP) $(CURDIR)/LICENSE $(PKG_BUILD_DIR)/
$(SHELL) $(CURDIR)/build-css.sh $(PKG_BUILD_DIR)/htdocs/luci-static/footstrap/cascade.css
# The private `--fs-*` names are 16% of the sheet and mean nothing to a browser — the same trade
# terser makes for the JS. BEFORE strip-templates.sh on purpose: the reserved set is derived by
# reading the JS and the templates, so it must see them whole. Over-reserving costs bytes,
# under-reserving breaks the theme silently.
# The reserved set is read from $(CURDIR), the SOURCE, never from $(PKG_BUILD_DIR): in CI the
# build tree's JS has already been through terser, so its comments are gone and five names that
# are only MENTIONED in a comment stop being reserved. That made the shipped sheet depend on WHO
# built it — 10 reserved names via CI, 15 via a plain SDK build, same source. Reading the source
# over-reserves by ~1 KB and is the safe direction.
$(SHELL) $(CURDIR)/mangle-tokens.sh \
$(PKG_BUILD_DIR)/htdocs/luci-static/footstrap/cascade.css \
$(CURDIR)/htdocs/luci-static/resources $(CURDIR)/ucode
# Templates are 58% comments and shipped them all. jsmin/terser strip the JS, build-css.sh the
# CSS; the .ut files were simply never included in that trade. Only `{# … #}` goes — see the
# script for why the ucode-code `/* … */` deliberately stays. -16 KB of 39, git keeps every word,
# and dev-sync.sh does NOT run this: a router you are debugging on keeps its comments.
$(SHELL) $(CURDIR)/strip-templates.sh $(PKG_BUILD_DIR)/ucode
# …and the same for the shell under root/ (71% and 95% comment lines). Whole-line `#` only.
$(SHELL) $(CURDIR)/strip-shell.sh $(PKG_BUILD_DIR)/root
$(SED) "s#const FS_VERSION *= *'[^']*'#const FS_VERSION = '$(if $(PKG_VERSION),$(PKG_VERSION),$(PKG_SRC_VERSION))'#" \
$(PKG_BUILD_DIR)/htdocs/luci-static/resources/fs-version.js
endef
# Absolute, not a relative path: CI rsyncs this package into package/, not into the feed.
include $(TOPDIR)/feeds/luci/luci.mk
# THE LINE BELOW IS LOAD-BEARING AND IT IS NOT A SIGNATURE — do not delete it as boilerplate.
#
# include/scan.mk builds the package list by GREPPING the Makefiles, not by parsing them:
# find -L package -name Makefile | xargs grep -aHE 'call (Build/DefaultTargets|BuildPackage|KernelPackage)'
# A Makefile that does not match is not in the list, so it is never dumped, no
# CONFIG_PACKAGE_* symbol is emitted for it, and `make package/<name>/compile` answers
# "No rule to make target" — with no error naming the package anywhere in the build.
#
# This theme never calls BuildPackage itself; luci.mk does, at the include above. The grep
# cannot see that. So the only thing that puts this package into the SDK's list is the literal
# text on the next line — which is why every luci-* Makefile carries it.
# Deleting it as a useless comment is exactly what broke the build (both formats, silently).
# tools/scan-marker.sh re-derives the grep from scan.mk and fails the build if it goes missing.
# call BuildPackage - OpenWrt buildroot signature