iso: bake bakery apps into /usr/local

BOS opts in to bakery's system prefix so desktop apps live on @
and ride snapper/grub-btrfs snapshots. The builder home stays
~/.local; build-local.sh copies bins, share/data, and user units
onto the image. Per-user installed.json and the index cache stay
in skel. Recovery is still grub-btrfs, not snapper rollback.
This commit is contained in:
Breadway 2026-08-16 00:10:32 +08:00
parent 744f18cd90
commit 34043086b9
19 changed files with 218 additions and 103 deletions

View file

@ -0,0 +1,2 @@
# Bakery desktop apps live under /usr/local so they ride snapper @ snapshots.
prefix = "/usr/local"

View file

@ -425,9 +425,10 @@ if command -v ufw &>/dev/null; then
fi
# The whole bread ecosystem (bakery, bread, breadbar, breadbox, breadcrumbs,
# breadpad, bos-settings, breadhelp, ...) is bakery-managed, not pacman: the
# binaries and bakery manifest live in /etc/skel/.local (baked in at ISO
# build time from iso/bread-lockfile.toml) and are copied into the user's
# breadpad, bos-settings, breadhelp, ...) is bakery-managed, not pacman:
# binaries, share/data, and user units are baked into /usr/local and
# /usr/lib/systemd/user (system prefix). Per-user bakery state (installed.json
# + index cache) is seeded from /etc/skel/.local and copied into the user's
# home below, so the install works fully offline with no DNS for bakery.
# ---------------------------------------------------------------------------

View file

@ -7,8 +7,9 @@
# alongside BOS's own bos.desktop, and breadgreet's session picker matches by
# .desktop file stem — with no override it picks "hyprland.desktop" over
# "bos.desktop", which skips bos-session's PATH fixup (adds ~/.local/bin for
# the bakery bread apps; greetd starts no login shell, so /etc/profile.d is
# never sourced any other way). Confirmed via breadgreet's own test suite
# per-user tools; bakery apps are in /usr/local/bin). greetd starts no login
# shell, so /etc/profile.d is never sourced any other way. Confirmed via
# breadgreet's own test suite
# (sessions.rs: discover_prefers_configured_default_over_first_entry).
[sessions]

View file

@ -29,7 +29,7 @@ Include = /etc/pacman.d/mirrorlist
# Breadway custom repo — breadlock plus AUR republishes the ISO needs
# (calamares, zen-browser-bin, bibata-cursor-theme-bin, yay-bin,
# zsh-theme-powerlevel10k). bakery / breadbar / bos-settings / breadhelp
# are NOT here; they are bakery-baked into /etc/skel at ISO build time.
# are NOT here; they are bakery-baked into /usr/local at ISO build time.
#
# Packages are published to the Forgejo Arch registry (group "os") by the
# .forgejo/workflows/*.yml workflows in this repo (and breadlock's).

View file

@ -1,8 +1,8 @@
# Put the per-user bakery bin dir on PATH. The bread ecosystem (breadd, breadbar,
# breadbox, …) is installed there by bakery, and the Hyprland session launches
# them via `exec-once`, which resolves against the PATH it inherits from the
# login shell. Arch's stock /etc/profile does not add ~/.local/bin, so do it here
# for every login shell (live user and installed user alike).
# Keep ~/.local/bin on PATH for per-user tools. Arch already includes
# /usr/local/bin (where bakery desktop apps live on BOS). The Hyprland
# session resolves exec-once against the PATH it inherits from the login
# shell; Arch's stock /etc/profile does not add ~/.local/bin, so do it
# here for every login shell (live user and installed user alike).
case ":$PATH:" in
*":$HOME/.local/bin:"*) ;;
*) export PATH="$HOME/.local/bin:$PATH" ;;

View file

@ -124,8 +124,8 @@ hl.on("hyprland.start", function()
"gsettings set org.gnome.desktop.interface cursor-theme Bibata-Modern-Ice",
"gsettings set org.gnome.desktop.interface cursor-size 24",
-- Clipboard history is breadclipd, a bakery-managed systemd --user
-- service (auto-started via skel — see build-local.sh's service bake)
-- rather than an exec-once here.
-- service (auto-started from /usr/lib/systemd/user — see
-- build-local.sh's service bake) rather than an exec-once here.
"/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1",
"awww-daemon",
-- Set the default wallpaper once the daemon is up (retry until ready).
@ -140,8 +140,8 @@ hl.on("hyprland.start", function()
-- breadpaper reads) is baked into skel too, right beside colors.json.
-- pywal only runs for real once the user picks a wallpaper themselves.
[[bash -c 'until awww img /usr/share/backgrounds/bos/bread-background.png 2>/dev/null; do sleep 0.3; done']],
-- breadd runs as a systemd user service (~/.config/systemd/user/breadd.service,
-- enabled in skel). It autostarts at login but before Hyprland exists, so
-- breadd runs as a systemd user service (/usr/lib/systemd/user/breadd.service,
-- plus a skel copy). It autostarts at login but before Hyprland exists, so
-- push the compositor's Wayland env into the user manager and restart breadd
-- to pick it up — that's how it gets HYPRLAND_INSTANCE_SIGNATURE to talk to Hyprland.
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
@ -162,7 +162,8 @@ hl.on("hyprland.start", function()
-- breadbox-sync is a Type=oneshot systemd --user service
-- (WantedBy=default.target, no Hyprland IPC dependency) — it already
-- runs on login via the unit baked into skel, independent of this list.
-- runs on login via the unit baked into /usr/lib/systemd/user,
-- independent of this list.
local ok, extra = pcall(function()
return dofile(script_dir .. "system/autostart.lua")()
end)

View file

@ -3,8 +3,8 @@ Description=Bread Runtime Daemon
[Service]
Type=simple
# %h = the user's home — works for any account created from this skel.
ExecStart=%h/.local/bin/breadd
# System-prefix bakery install — same path for every account.
ExecStart=/usr/local/bin/breadd
Restart=on-failure
RestartSec=2
UMask=0077

View file

@ -89,7 +89,7 @@ alias alt-install='yay -S'
alias alt-uninstall='yay -R'
alias alt-srchpkg='yay -Ss'
# ~/.local/bin holds the bread* binaries baked in at build time.
# Per-user tools. Bakery desktop apps live in /usr/local/bin (already on PATH).
export PATH="$HOME/.local/bin:$PATH"
# Powerlevel10k prompt configuration.

View file

@ -2,11 +2,10 @@
# BOS graphical session launcher, run by greetd on the INSTALLED system after
# the user authenticates (see /etc/greetd/config.toml).
#
# greetd does not start a login shell, so /etc/profile.d is never sourced — which
# means ~/.local/bin (where bakery installs the bread ecosystem: breadd, breadbar,
# breadbox-sync, …) would be missing from PATH and the Hyprland `exec-once`
# launches would fail. Source the login profile here so PATH is correct, set the
# Wayland session hints, then hand off to Hyprland.
# greetd does not start a login shell, so /etc/profile.d is never sourced.
# Bakery desktop apps live in /usr/local/bin (already on Arch PATH). Source
# the login profile here so ~/.local/bin (per-user tools) is also on PATH,
# set the Wayland session hints, then hand off to Hyprland.
#
# Launched via start-hyprland (ships with the hyprland package) rather than the
# raw Hyprland binary — Hyprland upstream no longer recommends exec'ing it

View file

@ -8,11 +8,12 @@
# or other bakery desktop apps. Every transaction is
# snapshotted by snap-pac; recover via the GRUB "snapshots"
# submenu (grub-btrfs), not `snapper rollback`.
# 2. bakery — the bread ecosystem apps in ~/.local/bin (whatever `bakery list`
# 2. bakery — the bread ecosystem apps in /usr/local (whatever `bakery list`
# reports as installed — bakery, bread, breadbar, breadbox,
# breadcrumbs, breadpad, breadman, bread-theme, breadpaper,
# breadmon, breadsearch, breadclip, breadshot, bos-settings,
# breadhelp, ...).
# breadhelp, ...). Those bits live on @ and ride snapper
# root snapshots; recover via grub-btrfs, not `snapper rollback`.
#
# Best-effort: a failure in one channel doesn't abort the other.
set -uo pipefail
@ -20,12 +21,11 @@ set -uo pipefail
bold() { printf '\033[1m%s\033[0m\n' "$1"; }
# Timed snapper pre snapshot before either channel. snap-pac already
# snapshots root around pacman; bakery writes ~/.local/bin ($HOME / @home),
# which is outside that root snapshot. This extra snapshot is still
# best-effort and covers bakery $HOME updates as well as possible — a
# home config if the installer created one, otherwise the root timeline
# around the whole update. Never fail the update if snapper is missing
# or the create errors.
# snapshots root around pacman; bakery now writes /usr/local (on @), so
# that root snapshot includes the desktop apps. This extra snapshot is
# still best-effort — a home config if the installer created one (user
# bakery state), plus a root timeline around the whole update. Never
# fail the update if snapper is missing or the create errors.
if command -v snapper >/dev/null; then
if snapper -c home list >/dev/null 2>&1; then
snapper -c home create -t pre -c number \