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:
parent
744f18cd90
commit
34043086b9
19 changed files with 218 additions and 103 deletions
|
|
@ -52,4 +52,7 @@ There is no `dev` integration branch.
|
|||
- Don't bake an ISO (`sudo ./build-local.sh`) unless asked — lockfile/docs
|
||||
work does not require it.
|
||||
- Don't tell users to `snapper rollback` blindly; GRUB pins
|
||||
`rootflags=subvol=@`. Recovery is grub-btrfs reboot.
|
||||
`rootflags=subvol=@`. Recovery is grub-btrfs reboot. Bakery desktop
|
||||
apps on BOS are system-prefix `/usr/local` (`/etc/bakery/config.toml`);
|
||||
snapper `@` snapshots include them. Do not move those bits back to
|
||||
`~/.local` on the image (hermes / default bakery stay user-layout).
|
||||
|
|
|
|||
27
README.md
27
README.md
|
|
@ -14,8 +14,9 @@ wiring up dotfiles, no per-tool bakery installs.
|
|||
|
||||
- **Compositor**: Hyprland with a native-Lua config (`hyprland.lua`), curated
|
||||
keybinds, snappy animations, blur, and pywal-driven colours on a black base.
|
||||
- **bread ecosystem**, baked into `/etc/skel` from bakery-managed binaries
|
||||
(no network needed at install time): the `bread`/`breadd` automation daemon
|
||||
- **bread ecosystem**, baked into `/usr/local` from bakery-managed binaries
|
||||
(no network needed at install time; per-user bakery state is seeded in
|
||||
`/etc/skel`): the `bread`/`breadd` automation daemon
|
||||
(`bread-emit` / `bread-module-host` when the stable bread release publishes
|
||||
them), `breadbar` (status bar + notifications), `breadbox` (launcher),
|
||||
`breadclip` (clipboard history), `breadcrumbs` (Wi-Fi profiles),
|
||||
|
|
@ -58,7 +59,7 @@ wiring up dotfiles, no per-tool bakery installs.
|
|||
|
||||
| Channel | What |
|
||||
|---------|------|
|
||||
| **Bakery, required** | `bakery`, `bread` / `breadd`, `breadbar`, `breadbox` / `breadbox-sync`, `breadcrumbs`, `breadpad` / `breadman`, `breadpaper`, `bread-theme`, `breadmon`, `breadsearch` / `breadmill`, `breadclip` / `breadclipd`, `breadshot`, `bos-settings`, `breadhelp` (+ breadhelp content under `~/.local/share/breadhelp/`) |
|
||||
| **Bakery, required** | `bakery`, `bread` / `breadd`, `breadbar`, `breadbox` / `breadbox-sync`, `breadcrumbs`, `breadpad` / `breadman`, `breadpaper`, `bread-theme`, `breadmon`, `breadsearch` / `breadmill`, `breadclip` / `breadclipd`, `breadshot`, `bos-settings`, `breadhelp` (+ breadhelp content under `/usr/local/share/breadhelp/`) |
|
||||
| **Bakery, optional** | `bread-emit`, `bread-module-host` — baked when the verified stable index publishes them; skipped (not a failed bake) until bread ships them |
|
||||
| **pacman (`packages.x86_64`)** | `breadlock`, plus the rest of the distro (Hyprland, Calamares, Zen, …) |
|
||||
| **Not shipped** | `breadcast`, `breadarr` |
|
||||
|
|
@ -123,9 +124,15 @@ Push `origin` (and `github` when mirroring). Do not treat origin as GitHub.
|
|||
|
||||
## Building the ISO
|
||||
|
||||
`build-local.sh` builds the image natively (no container) and bakes this
|
||||
machine's bakery-installed bread binaries + breadhelp content into
|
||||
`/etc/skel`:
|
||||
`build-local.sh` builds the image natively (no container) and copies this
|
||||
machine's bakery-installed bread binaries + breadhelp content from the
|
||||
builder's `~/.local` into the image at `/usr/local` (bins, share/data,
|
||||
desktop files, licenses) and `/usr/lib/systemd/user` (units). Per-user
|
||||
bakery state (`installed.json` + index cache) is seeded in `/etc/skel`.
|
||||
BOS opts in via `/etc/bakery/config.toml` (`prefix = "/usr/local"`);
|
||||
default bakery without that file is still `~/.local`. Snapper `@`
|
||||
snapshots include `/usr/local`; recovery is still grub-btrfs, not
|
||||
`snapper rollback`.
|
||||
|
||||
```sh
|
||||
sudo ./build-local.sh # release-quality (xz squashfs)
|
||||
|
|
@ -190,8 +197,8 @@ Hyprland session in QEMU. The disk lives on NVMe (not the tmpfs `/tmp`) to
|
|||
avoid memory pressure.
|
||||
|
||||
Post-install, `scripts/smoke-test.sh` (run as the installed user) checks
|
||||
subvolumes, services, bakery bins, and breadhelp content under
|
||||
`~/.local/share/breadhelp/content`.
|
||||
subvolumes, services, bakery bins on PATH, and breadhelp content under
|
||||
`/usr/local/share/breadhelp/content`.
|
||||
|
||||
## bos-settings
|
||||
|
||||
|
|
@ -234,7 +241,7 @@ repo, not here.
|
|||
## The bread ecosystem
|
||||
|
||||
Everything below is a separate bakery-distributed project with its own repo
|
||||
and release cadence, baked into `/etc/skel` at ISO build time so a fresh
|
||||
and release cadence, baked into `/usr/local` at ISO build time so a fresh
|
||||
install has them all with no network round-trip. Some ship more than one
|
||||
binary from a single package — that's noted where it applies. Most have a
|
||||
corresponding **bos-settings** panel; this table is about *using* the app
|
||||
|
|
@ -258,7 +265,7 @@ directly.
|
|||
| `breadman` | The fuller notes manager view (browse/organize) — ships from the same `breadpad` package as a second binary | `SUPER+M` |
|
||||
| `breadclip` | Clipboard history. `breadclipd` is the background daemon that actually records history; `breadclip` is the GTK4 popup that browses it | `SUPER+V` / `SUPER+Shift+V` |
|
||||
| `breadsearch` | Semantic system-wide search (indexes files/notes, embeds locally — CPU/ROCm/CUDA backend configurable). `breadmill` is its indexing daemon. | via breadbox, or BOS Settings → File Search |
|
||||
| `breadhelp` | Onboarding + in-session help/cheatsheet. Content lives at `~/.local/share/breadhelp/content` (bakery `content.tar.gz`, baked into skel). | `SUPER+/` |
|
||||
| `breadhelp` | Onboarding + in-session help/cheatsheet. Content lives at `/usr/local/share/breadhelp/content` (bakery `content.tar.gz`, baked into the image). | `SUPER+/` |
|
||||
|
||||
**System**
|
||||
|
||||
|
|
|
|||
112
build-local.sh
112
build-local.sh
|
|
@ -41,13 +41,15 @@ if [ "${FAST_BUILD:-0}" = "1" ]; then
|
|||
fi
|
||||
grep airootfs_image_tool_options "$STAGE/profiledef.sh"
|
||||
|
||||
# --- Bake this machine's bakery-installed bread ecosystem into /etc/skel ------
|
||||
# --- Bake this machine's bakery-installed bread ecosystem into the image ------
|
||||
# The bread desktop apps are bakery-managed (release binaries from
|
||||
# dl.breadway.dev / GitHub), not pacman. bakery needs DNS at install time,
|
||||
# which the live/installed image doesn't have — so instead of running bakery
|
||||
# on the target, we copy the binaries + bakery manifest this builder already
|
||||
# has into skel. Every user created from skel then gets those versions fully
|
||||
# offline. Copied at build time so the binaries never bloat the git repo.
|
||||
# has. Builder home stays user-layout (~/.local); the *image* is system-prefix
|
||||
# /usr/local so apps live on @ and ride snapper/grub-btrfs snapshots.
|
||||
# installed.json + index cache stay per-user in skel. Copied at build time
|
||||
# so the binaries never bloat the git repo.
|
||||
#
|
||||
# CI should prefer the stable bakery index when populating the builder home.
|
||||
# Local builds still snapshot the builder. required_bins fail the bake if
|
||||
|
|
@ -97,9 +99,14 @@ BAKERY_BIN="$LAPTOP_HOME/.local/bin"
|
|||
BAKERY_STATE="$LAPTOP_HOME/.local/state/bakery"
|
||||
BAKERY_CACHE="$LAPTOP_HOME/.cache/bakery"
|
||||
BAKERY_SHARE="$LAPTOP_HOME/.local/share"
|
||||
SKEL="$STAGE/airootfs/etc/skel"
|
||||
AIROOTFS="$STAGE/airootfs"
|
||||
IMAGE_BIN="$AIROOTFS/usr/local/bin"
|
||||
IMAGE_SHARE="$AIROOTFS/usr/local/share"
|
||||
IMAGE_UNITS="$AIROOTFS/usr/lib/systemd/user"
|
||||
SKEL="$AIROOTFS/etc/skel"
|
||||
echo "=== baking bakery bread ecosystem from $LAPTOP_HOME ==="
|
||||
echo "lockfile: $LOCKFILE (${#REQUIRED_BINS[@]} required, ${#OPTIONAL_BINS[@]} optional)"
|
||||
echo "image prefix: /usr/local (bins $IMAGE_BIN, share $IMAGE_SHARE, units $IMAGE_UNITS)"
|
||||
|
||||
missing=()
|
||||
for b in "${REQUIRED_BINS[@]}"; do
|
||||
|
|
@ -124,9 +131,9 @@ for b in "${OPTIONAL_BINS[@]}"; do
|
|||
fi
|
||||
done
|
||||
|
||||
install -d -m 0755 "$SKEL/.local/bin" "$SKEL/.local/state/bakery" "$SKEL/.cache/bakery"
|
||||
install -d -m 0755 "$IMAGE_BIN" "$SKEL/.local/state/bakery" "$SKEL/.cache/bakery"
|
||||
for b in "${BREAD_BINS[@]}"; do
|
||||
install -m 0755 "$BAKERY_BIN/$b" "$SKEL/.local/bin/$b"
|
||||
install -m 0755 "$BAKERY_BIN/$b" "$IMAGE_BIN/$b"
|
||||
done
|
||||
|
||||
# Drop packages that are not in the lockfile (breadcast/breadarr must not
|
||||
|
|
@ -164,26 +171,27 @@ if [[ ! -f "$BAKERY_CACHE/index.json" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
install -m 0644 "$BAKERY_CACHE/index.json" "$SKEL/.cache/bakery/index.json"
|
||||
echo "baked bins: $(ls "$SKEL/.local/bin")"
|
||||
echo "baked bins: $(ls "$IMAGE_BIN")"
|
||||
|
||||
# --- Bake bakery data dirs the apps need offline ------------------------------
|
||||
# bakery extracts data_archive (breadhelp's content.tar.gz) to
|
||||
# ~/.local/share/<pkg>/ and writes desktop entries + licenses next to it.
|
||||
# Copy those — never laptop-local state (clipboard history, WebKit cache,
|
||||
# bread sync-repo, models).
|
||||
echo "=== baking bakery share/data into skel ==="
|
||||
# $prefix/share/<pkg>/ and writes desktop entries + licenses next to it.
|
||||
# Builder home is still ~/.local/share; copy into the image at
|
||||
# /usr/local/share. Never laptop-local state (clipboard history, WebKit
|
||||
# cache, bread sync-repo, models).
|
||||
echo "=== baking bakery share/data into /usr/local/share ==="
|
||||
BREADHELP_CONTENT="$BAKERY_SHARE/breadhelp/content"
|
||||
if [[ ! -d "$BREADHELP_CONTENT" ]]; then
|
||||
echo "ERROR: breadhelp content missing: $BREADHELP_CONTENT" >&2
|
||||
echo "bakery installs this from content.tar.gz into ~/.local/share/breadhelp/content" >&2
|
||||
echo "bakery installs this from content.tar.gz into ~/.local/share/breadhelp/content on the builder" >&2
|
||||
echo "A breadhelp binary without content is a hollow ISO." >&2
|
||||
exit 1
|
||||
fi
|
||||
install -d -m 0755 "$SKEL/.local/share"
|
||||
cp -a "$BAKERY_SHARE/breadhelp" "$SKEL/.local/share/breadhelp"
|
||||
echo " baked $SKEL/.local/share/breadhelp/content"
|
||||
install -d -m 0755 "$IMAGE_SHARE"
|
||||
cp -a "$BAKERY_SHARE/breadhelp" "$IMAGE_SHARE/breadhelp"
|
||||
echo " baked $IMAGE_SHARE/breadhelp/content"
|
||||
|
||||
python3 - "$BAKERY_CACHE/index.json" "$BAKERY_SHARE" "$SKEL/.local/share" "${BREAD_BINS[@]}" <<'PY'
|
||||
python3 - "$BAKERY_CACHE/index.json" "$BAKERY_SHARE" "$IMAGE_SHARE" "${BREAD_BINS[@]}" <<'PY'
|
||||
import json, os, shutil, sys
|
||||
index_path, src_share, dest_share, *bins = sys.argv[1:]
|
||||
wanted = set(bins)
|
||||
|
|
@ -242,14 +250,16 @@ PY
|
|||
# silently left out, so those daemons never start on a fresh install/live
|
||||
# boot until the user re-runs `bakery install` (which needs network).
|
||||
# Source of truth is the *filtered* installed.json we just wrote: only
|
||||
# lockfile packages. Copy each unit with ExecStart rewritten from this
|
||||
# laptop's literal home path to the portable `%h` specifier, and recreate
|
||||
# whichever *.target.wants enable symlink bakery created locally. Units
|
||||
# already committed by hand (breadd.service carries a
|
||||
# RuntimeDirectoryPreserve=yes fix not yet upstreamed) are left alone.
|
||||
echo "=== baking bakery service units into skel ==="
|
||||
# lockfile packages. Units go to /usr/lib/systemd/user with ExecStart
|
||||
# rewritten to /usr/local/bin (not %h/.local/bin). Recreate whichever
|
||||
# *.target.wants enable symlink bakery created locally (or that skel
|
||||
# already ships). Hand-committed skel units (breadd.service carries a
|
||||
# RuntimeDirectoryPreserve=yes fix not yet upstreamed) are the source
|
||||
# for that unit and also get their ExecStart rewritten in skel.
|
||||
echo "=== baking bakery service units into /usr/lib/systemd/user ==="
|
||||
SYSTEMD_USER_DIR="$LAPTOP_HOME/.config/systemd/user"
|
||||
SKEL_SYSTEMD="$SKEL/.config/systemd/user"
|
||||
install -d -m 0755 "$IMAGE_UNITS"
|
||||
mapfile -t SERVICE_UNITS < <(python3 - "$SKEL/.local/state/bakery/installed.json" <<'PY'
|
||||
import json, sys
|
||||
with open(sys.argv[1]) as f:
|
||||
|
|
@ -259,40 +269,72 @@ for pkg in d.get("packages", d).values():
|
|||
print(s["unit"] if isinstance(s, dict) else s)
|
||||
PY
|
||||
)
|
||||
rewrite_exec_start() {
|
||||
local src="$1" dest="$2"
|
||||
python3 - "$src" "$dest" <<'PY'
|
||||
import os, sys
|
||||
src, dest = sys.argv[1], sys.argv[2]
|
||||
text = open(src).read()
|
||||
lines = []
|
||||
for line in text.splitlines():
|
||||
if line.lstrip().startswith("ExecStart="):
|
||||
key, rest = line.split("=", 1)
|
||||
argv = rest.split()
|
||||
if argv:
|
||||
name = os.path.basename(argv[0])
|
||||
argv[0] = "/usr/local/bin/" + name
|
||||
line = key + "=" + " ".join(argv)
|
||||
lines.append(line)
|
||||
out = "\n".join(lines)
|
||||
if text.endswith("\n"):
|
||||
out += "\n"
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
with open(dest, "w") as f:
|
||||
f.write(out)
|
||||
PY
|
||||
}
|
||||
for unit in "${SERVICE_UNITS[@]}"; do
|
||||
[[ -n "$unit" ]] || continue
|
||||
if [[ -f "$SKEL_SYSTEMD/$unit" ]]; then
|
||||
echo " $unit already committed in skel, leaving as-is"
|
||||
continue
|
||||
fi
|
||||
src="$SKEL_SYSTEMD/$unit"
|
||||
echo " $unit using committed skel unit as source"
|
||||
else
|
||||
src="$SYSTEMD_USER_DIR/$unit"
|
||||
if [[ ! -f "$src" ]]; then
|
||||
echo "ERROR: $unit listed in bakery installed.json but not found at $src" >&2
|
||||
echo "Refusing to bake a skel whose daemons will never start." >&2
|
||||
echo "Refusing to bake an image whose daemons will never start." >&2
|
||||
exit 1
|
||||
fi
|
||||
install -d -m 0755 "$SKEL_SYSTEMD"
|
||||
sed "s#ExecStart=$LAPTOP_HOME/.local/bin/#ExecStart=%h/.local/bin/#" "$src" > "$SKEL_SYSTEMD/$unit"
|
||||
for wants_dir in "$SYSTEMD_USER_DIR"/*.target.wants; do
|
||||
fi
|
||||
rewrite_exec_start "$src" "$IMAGE_UNITS/$unit"
|
||||
if [[ -f "$SKEL_SYSTEMD/$unit" ]]; then
|
||||
rewrite_exec_start "$src" "$SKEL_SYSTEMD/$unit"
|
||||
fi
|
||||
for base in "$SYSTEMD_USER_DIR" "$SKEL_SYSTEMD"; do
|
||||
[[ -d "$base" ]] || continue
|
||||
for wants_dir in "$base"/*.target.wants; do
|
||||
[[ -e "$wants_dir" || -L "$wants_dir" ]] || continue
|
||||
[[ -L "$wants_dir/$unit" ]] || continue
|
||||
target_name="$(basename "$wants_dir")"
|
||||
install -d -m 0755 "$SKEL_SYSTEMD/$target_name"
|
||||
ln -sf "../$unit" "$SKEL_SYSTEMD/$target_name/$unit"
|
||||
install -d -m 0755 "$IMAGE_UNITS/$target_name"
|
||||
ln -sf "../$unit" "$IMAGE_UNITS/$target_name/$unit"
|
||||
done
|
||||
echo " baked $unit"
|
||||
done
|
||||
echo " baked $unit -> $IMAGE_UNITS/$unit"
|
||||
done
|
||||
|
||||
# mkarchiso resets every airootfs file to 0644, so executables must be declared
|
||||
# in profiledef.sh's file_permissions array or they ship non-executable and the
|
||||
# exec-once launches fail with "permission denied". Inject a 0755 entry for each
|
||||
# baked binary right after the array opener (keeps the binary list in one place).
|
||||
# baked bakery binary right after the array opener (bos-* bins are already
|
||||
# listed; keeps the bakery list in one place — the lockfile).
|
||||
perm_file="$(mktemp)"
|
||||
for b in "${BREAD_BINS[@]}"; do
|
||||
printf ' ["/etc/skel/.local/bin/%s"]="0:0:755"\n' "$b" >>"$perm_file"
|
||||
printf ' ["/usr/local/bin/%s"]="0:0:755"\n' "$b" >>"$perm_file"
|
||||
done
|
||||
sed -i "/^file_permissions=(/r $perm_file" "$STAGE/profiledef.sh"
|
||||
rm -f "$perm_file"
|
||||
echo "=== file_permissions after injection ==="; grep -A14 '^file_permissions=(' "$STAGE/profiledef.sh"
|
||||
echo "=== file_permissions after injection ==="; grep -A40 '^file_permissions=(' "$STAGE/profiledef.sh"
|
||||
|
||||
# Pin one timestamp for the whole build. Without this, mkarchiso derives the
|
||||
# boot-config UUID (%ARCHISO_UUID%) when it starts and the iso9660 volume UUID
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ submenu** (grub-btrfs), not `snapper rollback`.
|
|||
|
||||
BOS GRUB pins `rootflags=subvol=@`. `snapper rollback` swaps the default
|
||||
subvolume; the installed `grub.cfg` will still boot `@`. Pick the grub-btrfs
|
||||
entry so the kernel command line matches the snapshot you want.
|
||||
entry so the kernel command line matches the snapshot you want. Bakery
|
||||
desktop apps live under `/usr/local` on `@`, so those same snapshots
|
||||
include them.
|
||||
|
||||
If the system will not boot (lost EFI entry / broken GRUB), boot the live
|
||||
ISO and run `sudo bos-rescue`. It mounts `@` + the ESP and offers the same
|
||||
|
|
|
|||
2
iso/airootfs/etc/bakery/config.toml
Normal file
2
iso/airootfs/etc/bakery/config.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Bakery desktop apps live under /usr/local so they ride snapper @ snapshots.
|
||||
prefix = "/usr/local"
|
||||
|
|
@ -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.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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" ;;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Bakery binaries baked into the live/installed skel.
|
||||
# Bakery binaries baked into the live/installed image at /usr/local.
|
||||
#
|
||||
# build-local.sh and CI (scripts/ci-stage-bakery.py) read this file. A missing
|
||||
# *required* binary fails the bake: a hollow ISO is worse than a failed build.
|
||||
|
|
|
|||
|
|
@ -206,10 +206,10 @@ yay-bin
|
|||
# /etc/pam.d/breadlock). Everything else — bakery, bread/breadd/bread-emit/
|
||||
# bread-module-host, breadbar, breadbox, breadcrumbs, breadpad, breadpaper,
|
||||
# bread-theme, breadmon, breadsearch, breadclip, breadshot, bos-settings,
|
||||
# breadhelp — is bakery-managed and baked into /etc/skel/.local at ISO build
|
||||
# breadhelp — is bakery-managed and baked into /usr/local at ISO build
|
||||
# time from iso/bread-lockfile.toml (see build-local.sh). breadcast and
|
||||
# breadarr are not shipped. bos-settings/breadhelp desktop entries are
|
||||
# committed under iso/airootfs/etc/skel/.local/share/applications/. Runtime
|
||||
# also committed under iso/airootfs/etc/skel/.local/share/applications/. Runtime
|
||||
# deps stay listed even though no bread package depends on them via pacman
|
||||
# (gtk4, gtk4-layer-shell, webkit2gtk-4.1, iw, libpulse, librsvg, …).
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,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).
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
# Read-only checks that a builder home (and optionally a staged skel) has
|
||||
# Read-only checks that a builder home (and optionally a staged image) has
|
||||
# everything build-local.sh needs before mkarchiso. Exit non-zero on failure.
|
||||
#
|
||||
# Builder home stays user-layout (~/.local). The image is system-prefix
|
||||
# /usr/local; pass SKEL and/or AIROOTFS to check those destinations.
|
||||
#
|
||||
# LAPTOP_HOME=/build-home ./scripts/ci-verify-bake.sh
|
||||
# SKEL=/tmp/bos-iso-stage/airootfs/etc/skel ./scripts/ci-verify-bake.sh
|
||||
# AIROOTFS=/tmp/bos-iso-stage/airootfs ./scripts/ci-verify-bake.sh
|
||||
set -euo pipefail
|
||||
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
LOCKFILE="${LOCKFILE:-$REPO/iso/bread-lockfile.toml}"
|
||||
LAPTOP_HOME="${LAPTOP_HOME:-/build-home}"
|
||||
SKEL="${SKEL:-}"
|
||||
AIROOTFS="${AIROOTFS:-}"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
|
|
@ -112,21 +117,73 @@ else
|
|||
done
|
||||
fi
|
||||
|
||||
if [[ -n "$SKEL" ]]; then
|
||||
echo "== staged skel $SKEL =="
|
||||
for b in "${REQUIRED_BINS[@]}"; do
|
||||
check_exec "$SKEL/.local/bin/$b" "skel required bin $b"
|
||||
done
|
||||
check_dir "$SKEL/.local/share/breadhelp/content" "skel breadhelp content"
|
||||
check_file "$SKEL/.cache/bakery/index.json" "skel bakery index cache"
|
||||
for unit in "${UNITS[@]}"; do
|
||||
[[ -n "$unit" ]] || continue
|
||||
if [[ -f "$SKEL/.config/systemd/user/$unit" ]]; then
|
||||
ok "skel unit $unit"
|
||||
if [[ -n "$SKEL" && -z "$AIROOTFS" ]]; then
|
||||
if [[ -d "$SKEL/usr/local/bin" ]]; then
|
||||
AIROOTFS="$SKEL"
|
||||
SKEL="$AIROOTFS/etc/skel"
|
||||
elif [[ -d "$SKEL/../../usr/local" ]]; then
|
||||
AIROOTFS="$(cd "$SKEL/../.." && pwd)"
|
||||
fi
|
||||
elif [[ -n "$AIROOTFS" && -z "$SKEL" ]]; then
|
||||
SKEL="$AIROOTFS/etc/skel"
|
||||
fi
|
||||
|
||||
if [[ -n "$AIROOTFS" || -n "$SKEL" ]]; then
|
||||
if [[ -n "$AIROOTFS" ]]; then
|
||||
echo "== staged image $AIROOTFS =="
|
||||
check_file "$AIROOTFS/etc/bakery/config.toml" "bakery prefix config"
|
||||
if [[ -f "$AIROOTFS/etc/bakery/config.toml" ]] && grep -q 'prefix[[:space:]]*=[[:space:]]*"/usr/local"' "$AIROOTFS/etc/bakery/config.toml"; then
|
||||
ok "bakery prefix = /usr/local"
|
||||
else
|
||||
bad "skel unit missing: $SKEL/.config/systemd/user/$unit"
|
||||
bad "bakery prefix is not /usr/local in $AIROOTFS/etc/bakery/config.toml"
|
||||
fi
|
||||
for b in "${REQUIRED_BINS[@]}"; do
|
||||
check_exec "$AIROOTFS/usr/local/bin/$b" "image required bin $b"
|
||||
done
|
||||
check_dir "$AIROOTFS/usr/local/share/breadhelp/content" "image breadhelp content"
|
||||
fi
|
||||
if [[ -n "$SKEL" ]]; then
|
||||
echo "== staged skel $SKEL =="
|
||||
check_file "$SKEL/.cache/bakery/index.json" "skel bakery index cache"
|
||||
check_file "$SKEL/.local/state/bakery/installed.json" "skel bakery installed.json"
|
||||
for b in "${REQUIRED_BINS[@]}"; do
|
||||
if [[ -e "$SKEL/.local/bin/$b" ]]; then
|
||||
bad "skel still has bakery bin $b (belongs in /usr/local/bin)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
image_units_json=""
|
||||
if [[ -n "$SKEL" && -f "$SKEL/.local/state/bakery/installed.json" ]]; then
|
||||
image_units_json="$SKEL/.local/state/bakery/installed.json"
|
||||
fi
|
||||
if [[ -n "$image_units_json" ]]; then
|
||||
mapfile -t IMAGE_UNITS < <(python3 - "$image_units_json" <<'PY'
|
||||
import json, sys
|
||||
path = sys.argv[1]
|
||||
with open(path) as f:
|
||||
data = json.load(f)
|
||||
pkgs = data.get("packages", data)
|
||||
for pkg in pkgs.values():
|
||||
for s in pkg.get("services", []):
|
||||
print(s["unit"] if isinstance(s, dict) else s)
|
||||
PY
|
||||
)
|
||||
else
|
||||
IMAGE_UNITS=("${UNITS[@]}")
|
||||
fi
|
||||
if [[ -n "$AIROOTFS" ]]; then
|
||||
for unit in "${IMAGE_UNITS[@]}"; do
|
||||
[[ -n "$unit" ]] || continue
|
||||
check_file "$AIROOTFS/usr/lib/systemd/user/$unit" "image unit $unit"
|
||||
if [[ -f "$AIROOTFS/usr/lib/systemd/user/$unit" ]]; then
|
||||
if grep -q '^ExecStart=/usr/local/bin/' "$AIROOTFS/usr/lib/systemd/user/$unit"; then
|
||||
ok "image unit $unit ExecStart uses /usr/local/bin"
|
||||
elif grep -q '^ExecStart=' "$AIROOTFS/usr/lib/systemd/user/$unit"; then
|
||||
bad "image unit $unit ExecStart is not /usr/local/bin: $(grep '^ExecStart=' "$AIROOTFS/usr/lib/systemd/user/$unit")"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ check "bos-settings installed" "command -v bos-settings"
|
|||
echo "== breadhelp =="
|
||||
check "breadhelp installed" "command -v breadhelp"
|
||||
check "breadhelp content installed" \
|
||||
"[ -d \"\$HOME/.local/share/breadhelp/content\" ] || [ -d /etc/skel/.local/share/breadhelp/content ]"
|
||||
"[ -d /usr/local/share/breadhelp/content ] || [ -d \"\$HOME/.local/share/breadhelp/content\" ]"
|
||||
check "bos-netcheck present" "command -v bos-netcheck"
|
||||
check "bos-rescue present" "command -v bos-rescue"
|
||||
check "bos-first-boot present" "command -v bos-first-boot"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue