@@ -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"
0 commit comments