Skip to content

Commit 24a1a95

Browse files
Yury-MonZonalchark
authored andcommitted
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 5152965 commit 24a1a95

29 files changed

Lines changed: 204 additions & 78 deletions

File tree

debian-rk3576-img.yaml

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

4141
# Subvolume layout (no reformat). /boot is a TOP-LEVEL subvolume named "boot"
42-
# (not "@boot") so it sits outside @ -> a single shared /boot that root snapshots
43-
# never capture, and so U-Boot, which reads the default (top-level) subvolume,
44-
# resolves /boot/extlinux/... straight into it. Keep the default subvolume as the
45-
# top level (do NOT set-default @) or U-Boot won't see this sibling /boot.
42+
# (not "@boot") so it sits outside the per-profile root subvolumes -> a single shared
43+
# /boot that root snapshots never capture, and so U-Boot, which reads the default
44+
# (top-level) subvolume, resolves /boot/extlinux/... straight into it. Keep the default
45+
# subvolume as the top level (do NOT set a default subvolume) or U-Boot won't see /boot.
4646
- action: run
4747
description: Create subvolumes and mount the layout
4848
chroot: false
@@ -51,16 +51,16 @@ actions:
5151
ROOTPART="$({{ $rootdev }})"
5252
5353
# debos has the top level (subvolid=5) mounted here; create subvolumes on it.
54-
for sv in @ boot @home @snapshots @var-log @var-cache; do
54+
# @Minimal is the common base / build target; @snapshots holds runtime snapshots.
55+
for sv in @Minimal boot @home @snapshots @var-log @var-cache; do
5556
btrfs subvolume create "$IMAGEMNTDIR/$sv"
5657
done
5758
umount "$IMAGEMNTDIR"
5859
59-
# Mount @ at the mount point debos owns, then nest the rest under it.
60-
mount -t btrfs -o "{{ $btrfsopts }},subvol=@" "$ROOTPART" "$IMAGEMNTDIR"
60+
# Mount @Minimal at the mount point debos owns, then nest the rest under it.
61+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@Minimal" "$ROOTPART" "$IMAGEMNTDIR"
6162
mkdir -p "$IMAGEMNTDIR/boot" \
6263
"$IMAGEMNTDIR/home" \
63-
"$IMAGEMNTDIR/.snapshots" \
6464
"$IMAGEMNTDIR/var/log" \
6565
"$IMAGEMNTDIR/var/cache"
6666
mount -t btrfs -o "{{ $btrfsopts }},subvol=boot" "$ROOTPART" "$IMAGEMNTDIR/boot"
@@ -69,21 +69,20 @@ actions:
6969
# stored on the subvolume and inherited by files added later (kernel updates).
7070
btrfs property set "$IMAGEMNTDIR/boot" compression none
7171
mount -t btrfs -o "{{ $btrfsopts }},subvol=@home" "$ROOTPART" "$IMAGEMNTDIR/home"
72-
# @snapshots is mounted only for the build (to write the Factory Image); it's
73-
# not in fstab, so the .snapshots mountpoint is removed from @ before detach.
74-
mount -t btrfs -o "{{ $btrfsopts }},subvol=@snapshots" "$ROOTPART" "$IMAGEMNTDIR/.snapshots"
7572
mount -t btrfs -o "{{ $btrfsopts }},subvol=@var-log" "$ROOTPART" "$IMAGEMNTDIR/var/log"
7673
mount -t btrfs -o "{{ $btrfsopts }},subvol=@var-cache" "$ROOTPART" "$IMAGEMNTDIR/var/cache"
7774
78-
# debos writes the standard root line into /etc/fstab and root=UUID into the
79-
# cmdline; the root subvolume is selected by the appended rootflags=subvol=@.
75+
# debos writes the standard root line into /etc/fstab and root=UUID into the cmdline;
76+
# the root subvolume is selected by the appended rootflags. @Desktop is the default root
77+
# (the desktop); the other profiles' BLS entries append rootflags=subvol=@<profile>
78+
# and the last rootflags= on the line wins. (Deploy still happens into @Minimal.)
8079
- action: filesystem-deploy
81-
append-kernel-cmdline: rootflags=subvol=@ {{ $cmdline }}
80+
append-kernel-cmdline: rootflags=subvol=@Desktop {{ $cmdline }}
8281

8382
# Add fstab entries for the non-root subvolumes (debos only generated the root
84-
# line). The FSUUID is read back at build time. @snapshots is intentionally
85-
# omitted -- it holds the Factory Image and is mounted on demand for rollback,
86-
# never auto-mounted at runtime.
83+
# line). The FSUUID is read back at build time. These (/boot, /home, /var/log,
84+
# /var/cache) are shared by every profile root. @snapshots is intentionally omitted
85+
# -- it holds runtime snapshots, managed on demand, never auto-mounted.
8786
- action: run
8887
description: Append subvolume entries to /etc/fstab
8988
chroot: false
@@ -120,25 +119,174 @@ actions:
120119
chroot: true
121120
command: find /var/cache/linux-kernel -name linux-image-\*.deb -a ! -name \*-dbg_\* -print0 | xargs -0 apt install -y
122121

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

131-
# Unmount the nested subvolumes (deepest first), leaving @ mounted at
132-
# $IMAGEMNTDIR. debos' image-partition Cleanup unmounts @ and detaches the loop;
133-
# without this it would fail with EBUSY on the still-mounted children.
134146
- action: run
135-
description: Unmount nested subvolumes before debos detaches the loop
147+
description: Lock @Minimal_stock (RO), re-create @Minimal from it, free the mountpoint
136148
chroot: false
137149
command: |
138150
set -eu
139151
sync
152+
ROOTPART="$({{ $rootdev }})" # @Minimal is still mounted at $IMAGEMNTDIR
153+
# Mount the btrfs top level at a fixed path and KEEP it mounted for the whole profile
154+
# sequence: it is our stable handle for `findmnt --target` (device lookup then works in
155+
# every debos mode, whether or not $IMAGEMNTDIR is mounted) and where the subvols live.
156+
TOP="${IMAGEMNTDIR%/}.toplevel"
157+
mkdir -p "$TOP"
158+
mount -t btrfs -o subvolid=5 "$ROOTPART" "$TOP"
159+
btrfs subvolume snapshot -r "$TOP/@Minimal" "$TOP/@Minimal_stock"
160+
# release @Minimal and its nested submounts so we can replace it and profile
161+
# factories can mount here
140162
findmnt -rno TARGET --submounts "$IMAGEMNTDIR" \
141-
| awk -v root="$IMAGEMNTDIR" '$0 != root' \
142163
| awk '{ print length, $0 }' | sort -rn | cut -d" " -f2- \
143164
| while read -r mp; do umount "$mp"; done
144-
rmdir "$IMAGEMNTDIR/.snapshots"
165+
# Re-create @Minimal as a writable child of the golden base (parent = @Minimal_stock)
166+
# so it matches the other profiles -- which are snapshots of their _stock -- instead
167+
# of being the build origin with no parent.
168+
btrfs subvolume delete "$TOP/@Minimal"
169+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Minimal"
170+
171+
# --- @Desktop ------------------------------------------------------------------
172+
- action: run
173+
description: Snapshot writable @Desktop_stock and mount it
174+
chroot: false
175+
command: |
176+
set -eu
177+
TOP="${IMAGEMNTDIR%/}.toplevel"
178+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
179+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Desktop_stock"
180+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@Desktop_stock" "$ROOTPART" "$IMAGEMNTDIR"
181+
- action: apt
182+
description: KDE desktop packages
183+
recommends: false # image is minbase/no-recommends; deps are listed explicitly
184+
packages:
185+
- bluedevil
186+
- blueman
187+
- breeze-gtk-theme
188+
- kde-config-gtk-style
189+
- kde-config-screenlocker
190+
- kde-spectacle
191+
- kde-standard
192+
- konsole
193+
- kscreen
194+
- mpv
195+
- plasma-nm
196+
- plasma-systemmonitor
197+
- powerdevil
198+
- sddm
199+
- sddm-theme-breeze
200+
- systemsettings
201+
- action: overlay
202+
description: KDE profile config (sddm autologin, plasma, wallpaper)
203+
source: overlays/profile-kde
204+
destination: /
205+
- action: run
206+
description: Make the KDE desktop the default session on @Desktop
207+
chroot: true
208+
command: systemctl set-default graphical.target
209+
- action: run
210+
description: Lock @Desktop_stock RO and spawn writable @Desktop
211+
chroot: false
212+
command: |
213+
set -eu
214+
TOP="${IMAGEMNTDIR%/}.toplevel"
215+
umount "$IMAGEMNTDIR"
216+
btrfs property set "$TOP/@Desktop_stock" ro true
217+
btrfs subvolume snapshot "$TOP/@Desktop_stock" "$TOP/@Desktop"
218+
219+
# --- @TV-Media-Box ---------------------------------------------------------
220+
- action: run
221+
description: Snapshot writable @TV-Media-Box_stock and mount it
222+
chroot: false
223+
command: |
224+
set -eu
225+
TOP="${IMAGEMNTDIR%/}.toplevel"
226+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
227+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@TV-Media-Box_stock"
228+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@TV-Media-Box_stock" "$ROOTPART" "$IMAGEMNTDIR"
229+
- action: apt
230+
description: TV media box packages
231+
recommends: false
232+
packages:
233+
- cage
234+
- kodi
235+
- kodi-eventclients-kodi-send
236+
- action: overlay
237+
description: TV media box profile config (kodi, enabled under multi-user)
238+
source: overlays/profile-tv-media-box
239+
destination: /
240+
- action: run
241+
description: Lock @TV-Media-Box_stock RO and spawn writable @TV-Media-Box
242+
chroot: false
243+
command: |
244+
set -eu
245+
TOP="${IMAGEMNTDIR%/}.toplevel"
246+
umount "$IMAGEMNTDIR"
247+
btrfs property set "$TOP/@TV-Media-Box_stock" ro true
248+
btrfs subvolume snapshot "$TOP/@TV-Media-Box_stock" "$TOP/@TV-Media-Box"
249+
250+
# --- @Router ---------------------------------------------------------------
251+
- action: run
252+
description: Snapshot writable @Router_stock and mount it
253+
chroot: false
254+
command: |
255+
set -eu
256+
TOP="${IMAGEMNTDIR%/}.toplevel"
257+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
258+
btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Router_stock"
259+
mount -t btrfs -o "{{ $btrfsopts }},subvol=@Router_stock" "$ROOTPART" "$IMAGEMNTDIR"
260+
# @Router needs no packages beyond Minimal (NetworkManager + dnsmasq-base + wpa_supplicant
261+
# are common); it just adds the AP/WAN connections + ip-forwarding via the overlay. Its
262+
# default session stays multi-user (inherited); NM autoconnect brings the router up.
263+
- action: overlay
264+
description: Router profile config (NM AP/WAN connections, ip-forwarding)
265+
source: overlays/profile-router
266+
destination: /
267+
- action: run
268+
description: Fix NetworkManager connection permissions on @Router
269+
chroot: true
270+
command: chmod 600 /etc/NetworkManager/system-connections/*.nmconnection
271+
- action: run
272+
description: Lock @Router_stock RO and spawn writable @Router
273+
chroot: false
274+
command: |
275+
set -eu
276+
TOP="${IMAGEMNTDIR%/}.toplevel"
277+
umount "$IMAGEMNTDIR"
278+
btrfs property set "$TOP/@Router_stock" ro true
279+
btrfs subvolume snapshot "$TOP/@Router_stock" "$TOP/@Router"
280+
281+
# Unmount the top level and remount @Minimal at the mountpoint debos owns, so its
282+
# image-partition Cleanup can unmount it and detach the loop. (The profile builds left
283+
# $IMAGEMNTDIR unmounted, and the lingering top-level mount would hold the loop busy.)
284+
- action: run
285+
description: Unmount the top level and remount @Minimal for debos cleanup
286+
chroot: false
287+
command: |
288+
set -eu
289+
TOP="${IMAGEMNTDIR%/}.toplevel"
290+
ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")"
291+
umount "$TOP"; rmdir "$TOP"
292+
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
- htop
6258
- i2c-tools
6359
- inetutils-telnet
@@ -68,25 +64,17 @@ actions:
6864
- iputils-ping
6965
- iw
7066
- iwd
71-
- kde-config-gtk-style
72-
- kde-config-screenlocker
73-
- kde-spectacle
74-
- kde-standard
7567
- kmscube
76-
- kodi
77-
- kodi-eventclients-kodi-send
78-
- konsole
79-
- kscreen
8068
- less
8169
- libcanberra-pulse
70+
- libdrm-tests
8271
- libfreeaptx0
72+
- liblc3-1
8373
- libldacbt-abr2
8474
- libldacbt-enc2
85-
- liblc3-1
86-
- libsbc1
8775
- libopus0
76+
- libsbc1
8877
- libspa-0.2-bluetooth
89-
- libdrm-tests
9078
- libtypec-utils
9179
- libubootenv-tool
9280
- libusb-1.0-0-dev
@@ -109,10 +97,7 @@ actions:
10997
- p7zip
11098
- pciutils
11199
- pipewire-alsa
112-
- plasma-nm
113-
- plasma-systemmonitor
114100
- powercap-utils
115-
- powerdevil
116101
- pv
117102
- python3-evdev
118103
- python3-pandas
@@ -129,7 +114,6 @@ actions:
129114
- systemd-resolved
130115
- systemd-sysv
131116
- systemd-timesyncd
132-
- systemsettings
133117
- tcpdump
134118
- traceroute
135119
- udev
@@ -261,9 +245,6 @@ actions:
261245
- libavformat-extra
262246
- lpac
263247
- mesa-teflon-delegate
264-
- mpv
265-
- sddm
266-
- sddm-theme-breeze
267248

268249
- action: overlay
269250
description: Overlay Kodi Chromium kiosk launcher

0 commit comments

Comments
 (0)