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