Skip to content

Commit 40b804b

Browse files
committed
Migrate to per-profile subvolumes
Build a read-only @Minimal_stock golden base, then derive one read-only @<Profile>_stock per profile (Desktop, TV-Media-Box, Router) and a writable @<Profile> snapshot of each. @minimal itself is re-created as a writable child of @Minimal_stock so it matches the other profiles instead of being the parentless build origin. Profile-specific overlays move under overlays/profile-<name>/, and profile naming is finalised (stock bases, Desktop replaces KDE).
1 parent 90e94fc commit 40b804b

28 files changed

Lines changed: 199 additions & 69 deletions

File tree

debian-rk3576-img.yaml

Lines changed: 177 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ actions:
4242
options: [ compress=zstd, noatime, ssd, discard=async, x-systemd.growfs ]
4343

4444
# Subvolume layout (no reformat). /boot is a TOP-LEVEL subvolume named "boot"
45-
# (not "@boot") so it sits outside @ -> a single shared /boot that root snapshots
46-
# never capture, and so U-Boot, which reads the default (top-level) subvolume,
47-
# resolves /boot/extlinux/... straight into it. Keep the default subvolume as the
48-
# top level (do NOT set-default @) or U-Boot won't see this sibling /boot.
45+
# (not "@boot") so it sits outside the per-profile root subvolumes -> a single shared
46+
# /boot that root snapshots never capture, and so U-Boot, which reads the default
47+
# (top-level) subvolume, resolves /boot/extlinux/... straight into it. Keep the default
48+
# subvolume as the top level (do NOT set a default subvolume) or U-Boot won't see /boot.
4949
- action: run
5050
description: Create subvolumes and mount the layout
5151
chroot: false
@@ -54,16 +54,16 @@ actions:
5454
ROOTPART="$({{ $rootdev }})"
5555
5656
# debos has the top level (subvolid=5) mounted here; create subvolumes on it.
57-
for sv in @ boot @home @snapshots @var-log @var-cache; do
57+
# @Minimal is the common base / build target; @snapshots holds runtime snapshots.
58+
for sv in @Minimal boot @home @snapshots @var-log @var-cache; do
5859
btrfs subvolume create "$IMAGEMNTDIR/$sv"
5960
done
6061
umount "$IMAGEMNTDIR"
6162
62-
# Mount @ at the mount point debos owns, then nest the rest under it.
63-
mount -t btrfs -o "{{ $btrfsopts }},subvol=@" "$ROOTPART" "$IMAGEMNTDIR"
63+
# Mount @Minimal at the mount point debos owns, then nest the rest under it.
64+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@Minimal" "$ROOTPART" "$IMAGEMNTDIR"
6465
mkdir -p "$IMAGEMNTDIR/boot" \
6566
"$IMAGEMNTDIR/home" \
66-
"$IMAGEMNTDIR/.snapshots" \
6767
"$IMAGEMNTDIR/var/log" \
6868
"$IMAGEMNTDIR/var/cache"
6969
mount -t btrfs -o "{{ $btrfsopts }},subvol=boot" "$ROOTPART" "$IMAGEMNTDIR/boot"
@@ -72,21 +72,20 @@ actions:
7272
# stored on the subvolume and inherited by files added later (kernel updates).
7373
btrfs property set "$IMAGEMNTDIR/boot" compression none
7474
mount -t btrfs -o "{{ $btrfsopts }},subvol=@home" "$ROOTPART" "$IMAGEMNTDIR/home"
75-
# @snapshots is mounted only for the build (to write the Factory Image); it's
76-
# not in fstab, so the .snapshots mountpoint is removed from @ before detach.
77-
mount -t btrfs -o "{{ $btrfsopts }},subvol=@snapshots" "$ROOTPART" "$IMAGEMNTDIR/.snapshots"
7875
mount -t btrfs -o "{{ $btrfsopts }},subvol=@var-log" "$ROOTPART" "$IMAGEMNTDIR/var/log"
7976
mount -t btrfs -o "{{ $btrfsopts }},subvol=@var-cache" "$ROOTPART" "$IMAGEMNTDIR/var/cache"
8077
81-
# debos writes the standard root line into /etc/fstab and root=UUID into the
82-
# cmdline; the root subvolume is selected by the appended rootflags=subvol=@.
78+
# debos writes the standard root line into /etc/fstab and root=UUID into the cmdline;
79+
# the root subvolume is selected by the appended rootflags. @Desktop is the default root
80+
# (the desktop); the other profiles' BLS entries append rootflags=subvol=@<profile>
81+
# and the last rootflags= on the line wins. (Deploy still happens into @Minimal.)
8382
- action: filesystem-deploy
84-
append-kernel-cmdline: rootflags=subvol=@ {{ $cmdline }}
83+
append-kernel-cmdline: rootflags=subvol=@Desktop {{ $cmdline }}
8584

8685
# Add fstab entries for the non-root subvolumes (debos only generated the root
87-
# line). The FSUUID is read back at build time. @snapshots is intentionally
88-
# omitted -- it holds the Factory Image and is mounted on demand for rollback,
89-
# never auto-mounted at runtime.
86+
# line). The FSUUID is read back at build time. These (/boot, /home, /var/log,
87+
# /var/cache) are shared by every profile root. @snapshots is intentionally omitted
88+
# -- it holds runtime snapshots, managed on demand, never auto-mounted.
9089
- action: run
9190
description: Append subvolume entries to /etc/fstab
9291
chroot: false
@@ -123,25 +122,174 @@ actions:
123122
chroot: true
124123
command: find /var/cache/linux-kernel -name linux-image-\*.deb -a ! -name \*-dbg_\* -print0 | xargs -0 apt install -y
125124

126-
# Read-only "Factory Image" snapshot of @, taken once the rootfs and kernel are
127-
# fully deployed. It captures @ only -- the nested subvols (boot/home/var-*) are
128-
# separate and excluded.
125+
# Default session for every profile: the no-DE multi-user target, where the cog-seat1
126+
# kiosk runs (and @TV's kodi, enabled under multi-user). Only @Desktop overrides this to
127+
# graphical.target (for sddm); @Minimal/@Router/@TV all stay on multi-user.
129128
- action: run
130-
description: Create the read-only Factory Image snapshot of @
131-
chroot: false
132-
command: btrfs subvolume snapshot -r "$IMAGEMNTDIR" "$IMAGEMNTDIR/.snapshots/@_Factory-Image"
129+
description: Set the default boot target to multi-user (only @Desktop overrides)
130+
chroot: true
131+
command: systemctl set-default multi-user.target
132+
133+
# ===========================================================================
134+
# Stock (RO) bases + per-profile bootable roots.
135+
#
136+
# @Minimal_stock is the RO golden image of the common base. Each profile is a
137+
# WRITABLE snapshot of it (COW: shares the base, only profile packages add new extents),
138+
# mounted at $IMAGEMNTDIR so standard debos `apt`/`overlay` actions install into it;
139+
# it is then locked RO and the writable @<profile> bootable root is snapshotted from it.
140+
# BLS selects a profile via rootflags=subvol=@<profile>. The kernel lives in
141+
# @Minimal_stock (installed above), so every profile inherits it via COW. /boot,
142+
# @home and @var-* are separate top-level subvols shared by all profiles.
143+
#
144+
# KDE and TV-Media-Box install their packages (apt) + config (overlay); only @Desktop changes
145+
# its default target (to graphical, for sddm) -- TV/Router/Minimal stay on multi-user.
146+
# Router applies only its config overlay (NM AP/WAN + ip-forwarding); no packages needed.
147+
# ===========================================================================
133148

134-
# Unmount the nested subvolumes (deepest first), leaving @ mounted at
135-
# $IMAGEMNTDIR. debos' image-partition Cleanup unmounts @ and detaches the loop;
136-
# without this it would fail with EBUSY on the still-mounted children.
137149
- action: run
138-
description: Unmount nested subvolumes before debos detaches the loop
150+
description: Lock @Minimal_stock (RO), re-create @Minimal from it, free the mountpoint
139151
chroot: false
140152
command: |
141153
set -eu
142154
sync
155+
ROOTPART="$({{ $rootdev }})" # @Minimal is still mounted at $IMAGEMNTDIR
156+
# Mount the btrfs top level at a fixed path and KEEP it mounted for the whole profile
157+
# sequence: it is our stable handle for `findmnt --target` (device lookup then works in
158+
# every debos mode, whether or not $IMAGEMNTDIR is mounted) and where the subvols live.
159+
TOP="${IMAGEMNTDIR%/}.toplevel"
160+
mkdir -p "$TOP"
161+
mount -t btrfs -o subvolid=5 "$ROOTPART" "$TOP"
162+
btrfs subvolume snapshot -r "$TOP/@Minimal" "$TOP/@Minimal_stock"
163+
# release @Minimal and its nested submounts so we can replace it and profile
164+
# factories can mount here
143165
findmnt -rno TARGET --submounts "$IMAGEMNTDIR" \
144-
| awk -v root="$IMAGEMNTDIR" '$0 != root' \
145166
| awk '{ print length, $0 }' | sort -rn | cut -d" " -f2- \
146167
| while read -r mp; do umount "$mp"; done
147-
rmdir "$IMAGEMNTDIR/.snapshots"
168+
# Re-create @Minimal as a writable child of the golden base (parent = @Minimal_stock)
169+
# so it matches the other profiles -- which are snapshots of their _stock -- instead
170+
# of being the build origin with no parent.
171+
btrfs subvolume delete "$TOP/@Minimal"
172+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Minimal"
173+
174+
# --- @Desktop ------------------------------------------------------------------
175+
- action: run
176+
description: Snapshot writable @Desktop_stock and mount it
177+
chroot: false
178+
command: |
179+
set -eu
180+
TOP="${IMAGEMNTDIR%/}.toplevel"
181+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
182+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Desktop_stock"
183+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@Desktop_stock" "$ROOTPART" "$IMAGEMNTDIR"
184+
- action: apt
185+
description: KDE desktop packages
186+
recommends: false # image is minbase/no-recommends; deps are listed explicitly
187+
packages:
188+
- bluedevil
189+
- blueman
190+
- breeze-gtk-theme
191+
- kde-config-gtk-style
192+
- kde-config-screenlocker
193+
- kde-spectacle
194+
- kde-standard
195+
- konsole
196+
- kscreen
197+
- mpv
198+
- plasma-nm
199+
- plasma-systemmonitor
200+
- powerdevil
201+
- sddm
202+
- sddm-theme-breeze
203+
- systemsettings
204+
- action: overlay
205+
description: KDE profile config (sddm autologin, plasma, wallpaper)
206+
source: overlays/profile-kde
207+
destination: /
208+
- action: run
209+
description: Make the KDE desktop the default session on @Desktop
210+
chroot: true
211+
command: systemctl set-default graphical.target
212+
- action: run
213+
description: Lock @Desktop_stock RO and spawn writable @Desktop
214+
chroot: false
215+
command: |
216+
set -eu
217+
TOP="${IMAGEMNTDIR%/}.toplevel"
218+
umount "$IMAGEMNTDIR"
219+
btrfs property set "$TOP/@Desktop_stock" ro true
220+
btrfs subvolume snapshot "$TOP/@Desktop_stock" "$TOP/@Desktop"
221+
222+
# --- @TV-Media-Box ---------------------------------------------------------
223+
- action: run
224+
description: Snapshot writable @TV-Media-Box_stock and mount it
225+
chroot: false
226+
command: |
227+
set -eu
228+
TOP="${IMAGEMNTDIR%/}.toplevel"
229+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
230+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@TV-Media-Box_stock"
231+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@TV-Media-Box_stock" "$ROOTPART" "$IMAGEMNTDIR"
232+
- action: apt
233+
description: TV media box packages
234+
recommends: false
235+
packages:
236+
- cage
237+
- kodi
238+
- kodi-eventclients-kodi-send
239+
- action: overlay
240+
description: TV media box profile config (kodi, enabled under multi-user)
241+
source: overlays/profile-tv-media-box
242+
destination: /
243+
- action: run
244+
description: Lock @TV-Media-Box_stock RO and spawn writable @TV-Media-Box
245+
chroot: false
246+
command: |
247+
set -eu
248+
TOP="${IMAGEMNTDIR%/}.toplevel"
249+
umount "$IMAGEMNTDIR"
250+
btrfs property set "$TOP/@TV-Media-Box_stock" ro true
251+
btrfs subvolume snapshot "$TOP/@TV-Media-Box_stock" "$TOP/@TV-Media-Box"
252+
253+
# --- @Router ---------------------------------------------------------------
254+
- action: run
255+
description: Snapshot writable @Router_stock and mount it
256+
chroot: false
257+
command: |
258+
set -eu
259+
TOP="${IMAGEMNTDIR%/}.toplevel"
260+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
261+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Router_stock"
262+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@Router_stock" "$ROOTPART" "$IMAGEMNTDIR"
263+
# @Router needs no packages beyond Minimal (NetworkManager + dnsmasq-base + wpa_supplicant
264+
# are common); it just adds the AP/WAN connections + ip-forwarding via the overlay. Its
265+
# default session stays multi-user (inherited); NM autoconnect brings the router up.
266+
- action: overlay
267+
description: Router profile config (NM AP/WAN connections, ip-forwarding)
268+
source: overlays/profile-router
269+
destination: /
270+
- action: run
271+
description: Fix NetworkManager connection permissions on @Router
272+
chroot: true
273+
command: chmod 600 /etc/NetworkManager/system-connections/*.nmconnection
274+
- action: run
275+
description: Lock @Router_stock RO and spawn writable @Router
276+
chroot: false
277+
command: |
278+
set -eu
279+
TOP="${IMAGEMNTDIR%/}.toplevel"
280+
umount "$IMAGEMNTDIR"
281+
btrfs property set "$TOP/@Router_stock" ro true
282+
btrfs subvolume snapshot "$TOP/@Router_stock" "$TOP/@Router"
283+
284+
# Unmount the top level and remount @Minimal at the mountpoint debos owns, so its
285+
# image-partition Cleanup can unmount it and detach the loop. (The profile builds left
286+
# $IMAGEMNTDIR unmounted, and the lingering top-level mount would hold the loop busy.)
287+
- action: run
288+
description: Unmount the top level and remount @Minimal for debos cleanup
289+
chroot: false
290+
command: |
291+
set -eu
292+
TOP="${IMAGEMNTDIR%/}.toplevel"
293+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
294+
umount "$TOP"; rmdir "$TOP"
295+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@Minimal" "$ROOTPART" "$IMAGEMNTDIR"

debian-rk3576-ospack.yaml

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,20 @@ actions:
1919
include:
2020
- acl
2121
- adduser
22-
- avahi-daemon
2322
- alsa-ucm-conf
2423
- alsa-utils
2524
- apt-transport-https
25+
- avahi-daemon
2626
- bash-completion
2727
- bc
28-
- bluedevil
29-
- blueman
3028
- bluez
3129
- bluez-firmware
3230
- bluez-meshd
33-
- breeze-gtk-theme
3431
- bsdextrautils
3532
- bsdmainutils
3633
- btrfs-compsize
3734
- btrfs-progs
3835
- build-essential
39-
- cage
4036
- ca-certificates
4137
- cec-utils
4238
- chromium
@@ -46,8 +42,6 @@ actions:
4642
- dnsmasq-base
4743
- drm-info
4844
- duperemove
49-
- gstreamer1.0-plugins-bad
50-
- gstreamer1.0-tools
5145
- e2fsprogs
5246
- edid-decode
5347
- evtest
@@ -58,6 +52,8 @@ actions:
5852
- git
5953
- glmark2-wayland
6054
- gpiod
55+
- gstreamer1.0-plugins-bad
56+
- gstreamer1.0-tools
6157
- i2c-tools
6258
- inetutils-telnet
6359
- initramfs-tools
@@ -67,25 +63,17 @@ actions:
6763
- iputils-ping
6864
- iw
6965
- iwd
70-
- kde-config-gtk-style
71-
- kde-config-screenlocker
72-
- kde-spectacle
73-
- kde-standard
7466
- kmscube
75-
- kodi
76-
- kodi-eventclients-kodi-send
77-
- konsole
78-
- kscreen
7967
- less
8068
- libcanberra-pulse
69+
- libdrm-tests
8170
- libfreeaptx0
71+
- liblc3-1
8272
- libldacbt-abr2
8373
- libldacbt-enc2
84-
- liblc3-1
85-
- libsbc1
8674
- libopus0
75+
- libsbc1
8776
- libspa-0.2-bluetooth
88-
- libdrm-tests
8977
- libtypec-utils
9078
- libubootenv-tool
9179
- libusb-1.0-0-dev
@@ -107,10 +95,7 @@ actions:
10795
- p7zip
10896
- pciutils
10997
- pipewire-alsa
110-
- plasma-nm
111-
- plasma-systemmonitor
11298
- powercap-utils
113-
- powerdevil
11499
- python3-evdev
115100
- python3-pandas
116101
- python3-plotly
@@ -126,7 +111,6 @@ actions:
126111
- systemd-resolved
127112
- systemd-sysv
128113
- systemd-timesyncd
129-
- systemsettings
130114
- tcpdump
131115
- traceroute
132116
- udev
@@ -258,9 +242,6 @@ actions:
258242
- libavformat-extra
259243
- lpac
260244
- mesa-teflon-delegate
261-
- mpv
262-
- sddm
263-
- sddm-theme-breeze
264245

265246
- action: overlay
266247
description: Overlay Kodi Chromium kiosk launcher

0 commit comments

Comments
 (0)