Skip to content

Commit 53d5459

Browse files
fix(grub,keyring): auto-apply GRUB font via a boot service; fix two helper bugs (#172)
Two bugs surfaced running the new recipes on a real (candidate.20260616) host: 1) `ujust margine-grub-hidpi` failed: `bootupctl backend install --with-static-configs` refuses once installed ("invalid re-install attempted: /boot/bootupd-state.json already exists"). bootupd has NO supported re-render command. Replace it with bootupd's own composition — `cat grub-static-pre.cfg configs.d/*.cfg > /boot/grub2/grub.cfg` — guarded by grub2-script-check + blscfg/font sanity + a backup + atomic write, and made idempotent (no-op when grub.cfg/font already match). Validated off-boot: the render passes grub2-script-check and carries blscfg + margine.pf2. 2) `ujust margine-keyring` (default `status`) printed correctly but exited 1: `[[ -n "$bak" ]] && echo` trips `set -e` when no backup exists. Use an `if`. Also — answer "shouldn't this just be in the image?": the GRUB config/font ARE baked, but the bootloader on /boot is per-install and NOT updated by image upgrades. So make it AUTOMATIC instead of a manual ujust: add margine-grub-hidpi.service (oneshot, enabled via multi-user.target.wants) that runs the idempotent script at boot — the baked GRUB config now lands on /boot after an update with no manual step. The ujust recipe stays as a force/inspect helper. (Keyring stays opt-in by design: per-user $HOME state, encrypted with the login password, and a security-reducing choice that needs consent.) Layer A: assert the service ships AND is enabled (the .wants symlink).
1 parent 500a174 commit 53d5459

6 files changed

Lines changed: 87 additions & 34 deletions

File tree

.github/scripts/validate-image-rootfs.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ GRUB_GFX="usr/lib/bootupd/grub2-static/configs.d/05_margine-gfxmode.cfg"
8585
check_file "$GRUB_GFX" "A.4.gfx"
8686
check_nonempty "usr/lib/bootupd/grub2-static/fonts/margine.pf2" "A.4.gfx"
8787
check_exec "usr/libexec/margine/grub-hidpi-apply" "A.4.gfx"
88+
# The re-render runs automatically at boot (the bootloader on /boot isn't
89+
# updated by image upgrades) — assert the service ships AND is enabled.
90+
check_file "usr/lib/systemd/system/margine-grub-hidpi.service" "A.4.gfx"
91+
test -L "$ROOTFS/usr/lib/systemd/system/multi-user.target.wants/margine-grub-hidpi.service" \
92+
|| { echo "::error::A.4.gfx margine-grub-hidpi.service is not enabled (multi-user.target.wants symlink missing) — GRUB font won't auto-apply"; fail=1; }
8893
grep -q 'loadfont .*margine\.pf2' "$ROOTFS/$GRUB_GFX" 2>/dev/null \
8994
|| { echo "::error::A.4.gfx GRUB drop-in does not loadfont the baked margine.pf2 — menu stays tiny on HiDPI"; fail=1; }
9095
grep -q '^[[:space:]]*set gfxterm_font=' "$ROOTFS/$GRUB_GFX" 2>/dev/null \

build_files/60-custom.just

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ margine-autologin ACTION="status" USER="":
664664
margine-tpm-unlock ACTION="status" PCRS="7":
665665
sudo /usr/bin/margine-tpm-unlock {{ ACTION }} {{ PCRS }}
666666

667-
# Make the GRUB boot menu legible on HiDPI panels: copy the baked large font onto /boot and re-render /boot/grub2/grub.cfg from the image. Run ONCE after `ujust margine-update`, then reboot. (An upgrade alone does NOT re-render the bootloader config — bootupd only does that at install time, so new font/gfx settings never reach /boot otherwise.)
667+
# Force/inspect the HiDPI GRUB font + grub.cfg render NOW. Normally you don't need this: margine-grub-hidpi.service applies it automatically at boot whenever the image's baked GRUB config changes (the bootloader on /boot isn't updated by image upgrades, so a boot-time service closes the gap). Run this only to apply it immediately after an update instead of waiting for the next reboot. Safe + idempotent (grub2-script-check + backup; no-op when already current).
668668
[group('Margine')]
669669
margine-grub-hidpi:
670670
@sudo /usr/libexec/margine/grub-hidpi-apply

build_files/system_files/usr/bin/margine-keyring

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ status() {
4646
echo " file: $LOGIN ($(stat -c '%s bytes, mode %a' "$LOGIN" 2>/dev/null))"
4747
local bak
4848
bak="$(ls -1t "$LOGIN".margine.bak.* 2>/dev/null | head -1 || true)"
49-
[[ -n "$bak" ]] && echo " backup: $bak (restore with: ujust margine-keyring restore)"
49+
# NB: plain `[[ -n "$bak" ]] && echo` would make status() exit non-zero under
50+
# `set -e` when there is no backup — use an if so `ujust margine-keyring`
51+
# (default action) always exits 0.
52+
if [[ -n "$bak" ]]; then echo " backup: $bak (restore with: ujust margine-keyring restore)"; fi
5053
}
5154

5255
blank() {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[Unit]
2+
Description=Margine: apply the HiDPI GRUB font + re-render grub.cfg when the baked config changes
3+
Documentation=https://github.com/daniel-g-carrasco/margine-image
4+
# The bootloader lives on /boot (+ESP); bootc/bootupd only write it at install,
5+
# not on image upgrades. This oneshot closes that gap: it renders the image's
6+
# baked GRUB static config onto /boot whenever it differs (idempotent — a no-op
7+
# on every boot where nothing changed). Needs /boot mounted; skips itself if the
8+
# image ships no baked font.
9+
RequiresMountsFor=/boot
10+
ConditionPathExists=/usr/lib/bootupd/grub2-static/fonts/margine.pf2
11+
After=local-fs.target
12+
13+
[Service]
14+
Type=oneshot
15+
# Idempotent + self-guarding (grub2-script-check + backup + atomic write); the
16+
# script no-ops unless the rendered grub.cfg/font would differ from /boot.
17+
ExecStart=/usr/libexec/margine/grub-hidpi-apply --quiet
18+
19+
[Install]
20+
WantedBy=multi-user.target
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../margine-grub-hidpi.service
Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,65 @@
11
#!/usr/bin/env bash
2-
# Make the baked HiDPI GRUB font live on an EXISTING install.
2+
# Apply the baked HiDPI GRUB font + render /boot/grub2/grub.cfg from the image's
3+
# bootupd static config. Idempotent — re-renders ONLY when the result would
4+
# differ from what's already on /boot.
35
#
4-
# WHY THIS EXISTS: bootupd renders /boot/grub2/grub.cfg from the image's
5-
# /usr/lib/bootupd/grub2-static/{grub-static-pre.cfg,configs.d/*,grub-static-
6-
# efi.cfg} ONLY at OS install time (or `migrate-static-grub-config`). A normal
7-
# `bootc upgrade` / `rpm-ostree upgrade` does NOT re-render it, and `bootupctl
8-
# update` only refreshes the EFI binaries — neither rewrites grub.cfg nor
9-
# copies the font to /boot. So a new font / drop-in shipped in the image never
10-
# reaches the boot partition until this runs. Fresh installs pick it up at
11-
# install; existing installs run this once (via `ujust margine-grub-hidpi`).
6+
# Runs AUTOMATICALLY at boot via margine-grub-hidpi.service (so a new baked
7+
# GRUB config lands after an image update with no manual step) and on demand
8+
# via `ujust margine-grub-hidpi`.
129
#
13-
# Idempotent. Reversible: the font/config live in the image (rpm-ostree
14-
# rollback reverts the rootfs side); re-running this re-renders from whatever
15-
# image is currently deployed.
10+
# WHY a per-install step exists at all: the OS image is immutable, but the
11+
# BOOTLOADER lives on /boot (+ the ESP), which bootc/bootupd write at INSTALL
12+
# time and do NOT update on `rpm-ostree upgrade` / `bootc upgrade` (bootupd
13+
# update refreshes only the EFI binaries, never re-renders grub.cfg). So the
14+
# baked grub.cfg/font never reach /boot on an upgrade by themselves. There is
15+
# no supported bootupd re-render command — `bootupctl backend install` refuses
16+
# once /boot/bootupd-state.json exists. So we replicate bootupd's own
17+
# composition (grub-static-pre.cfg + configs.d/*.cfg, glob-sorted), guarded by
18+
# grub2-script-check + a backup + atomic write so it can never leave an
19+
# unbootable grub.cfg.
1620
set -euo pipefail
1721

22+
QUIET=0; [[ "${1:-}" == "--quiet" ]] && QUIET=1
23+
say() { [[ "$QUIET" -eq 1 ]] || printf '%s\n' "$*"; }
24+
1825
[[ "$(id -u)" -eq 0 ]] || { echo "ERROR: must run as root (use: ujust margine-grub-hidpi)"; exit 1; }
1926

20-
FONT_SRC=/usr/lib/bootupd/grub2-static/fonts/margine.pf2
21-
if [[ ! -f "$FONT_SRC" ]]; then
22-
echo "ERROR: baked GRUB font missing ($FONT_SRC)."
23-
echo " This deployment predates the HiDPI GRUB font — run 'ujust margine-update' first."
24-
exit 1
27+
SRC=/usr/lib/bootupd/grub2-static
28+
FONT_SRC="$SRC/fonts/margine.pf2"
29+
FONT_DST=/boot/grub2/fonts/margine.pf2
30+
CFG=/boot/grub2/grub.cfg
31+
32+
# Image without the baked font (e.g. rolled back below #168) → nothing to do.
33+
[[ -f "$FONT_SRC" ]] || { say "no baked GRUB font in this image — nothing to do"; exit 0; }
34+
# Need the static parts + a /boot/grub2 (skip quietly on odd layouts).
35+
[[ -f "$SRC/grub-static-pre.cfg" && -d "$SRC/configs.d" && -d /boot/grub2 ]] \
36+
|| { say "bootupd static config or /boot/grub2 not present — skipping"; exit 0; }
37+
38+
# Render exactly as bootupd does for /boot/grub2/grub.cfg: pre + configs.d/*
39+
# (the shell glob sorts the same way bootupd does). The ESP's
40+
# grub-static-efi.cfg has no font logic, so we never touch it.
41+
tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT
42+
cat "$SRC/grub-static-pre.cfg" "$SRC"/configs.d/*.cfg > "$tmp"
43+
44+
# Idempotent fast path (the common case every boot): font already on /boot and
45+
# grub.cfg already equals the render → nothing to do.
46+
if [[ -f "$FONT_DST" ]] && cmp -s "$FONT_SRC" "$FONT_DST" && cmp -s "$tmp" "$CFG"; then
47+
say "GRUB HiDPI font + grub.cfg already current — nothing to do"
48+
exit 0
2549
fi
2650

27-
# 1) Put the font on /boot, where GRUB ($prefix=/boot/grub2) can loadfont it.
28-
# bootupd renders grub.cfg but does NOT ship this font, so copy it ourselves.
29-
echo "Installing $FONT_SRC -> /boot/grub2/fonts/margine.pf2"
30-
mkdir -p /boot/grub2/fonts
31-
install -m0644 "$FONT_SRC" /boot/grub2/fonts/margine.pf2
32-
33-
# 2) Re-render /boot/grub2/grub.cfg (+ the ESP's grub-static-efi.cfg) from the
34-
# current image's static drop-ins. `backend install --with-static-configs`
35-
# is the only bootupd path that re-applies configs.d/* after install;
36-
# --filesystem /boot lets bootupd discover the backing ESP(s). DEST_ROOT=/
37-
# is the running system; src-root defaults to /.
38-
echo "Re-rendering /boot/grub2/grub.cfg from the image static config..."
39-
/usr/bin/bootupctl backend install --with-static-configs --filesystem /boot /
40-
41-
echo "Done. Reboot to see the larger boot menu: systemctl reboot"
51+
# Safety gate: never install a grub.cfg that fails syntax or is missing the
52+
# load-bearing pieces (BLS menu import + our font).
53+
grub2-script-check "$tmp" || { echo "ERROR: rendered grub.cfg failed grub2-script-check — refusing to install"; exit 1; }
54+
grep -q 'blscfg' "$tmp" || { echo "ERROR: rendered grub.cfg lacks blscfg (no menu entries) — refusing"; exit 1; }
55+
grep -q 'margine.pf2' "$tmp" || { echo "ERROR: rendered grub.cfg lacks the margine font — refusing"; exit 1; }
56+
57+
# 1) Font onto /boot (GRUB $prefix=/boot/grub2 at boot).
58+
install -D -m0644 "$FONT_SRC" "$FONT_DST"
59+
say "installed $FONT_DST"
60+
61+
# 2) grub.cfg: back up the current one, then atomically replace.
62+
[[ -f "$CFG" ]] && cp -a "$CFG" "$CFG.margine.bak"
63+
install -m0644 "$tmp" "$CFG"
64+
say "re-rendered $CFG (backup: $CFG.margine.bak)"
65+
say "Done. The larger boot menu shows on the NEXT reboot."

0 commit comments

Comments
 (0)