| 707 |
707 |
|
# starship, satty, and some others). Does not carry Rust binaries like
|
| 708 |
708 |
|
# nushell or yazi; COPRs below handle those.
|
| 709 |
709 |
|
# https://terra.fyralabs.com/
|
|
710 |
+ |
#
|
|
711 |
+ |
# `dnf clean all` matters more here than the install does. terra-release is
|
|
712 |
+ |
# 674 BYTES; the layer measured 238 MB, and every byte of the difference was
|
|
713 |
+ |
# repo metadata in /var/cache/libdnf5 (fedora 64 MB, updates 37 MB,
|
|
714 |
+ |
# updates-archive 43 MB, terra 3.7 MB, and their solv caches). A later layer's
|
|
715 |
+ |
# `dnf clean all` deletes it, which is why the built image shows 28 KB of
|
|
716 |
+ |
# cache and the size stays: a delete in a later layer is a whiteout, not a
|
|
717 |
+ |
# refund. Cleaning inside the layer that filled it is the only thing that
|
|
718 |
+ |
# reclaims the bytes.
|
|
719 |
+ |
#
|
|
720 |
+ |
# It does not merely move the cost to the next layer. Measured 2026-08-17 on
|
|
721 |
+ |
# fedora-bootc, this three-layer chain against itself:
|
|
722 |
+ |
# as it was: 188 MB + 41.2 MB + 42.5 MB = 272 MB
|
|
723 |
+ |
# as it is: 34.5 MB + 37 MB + 42.8 MB = 114 MB
|
|
724 |
+ |
# The next dnf line re-downloads the metadata and deletes it in the same
|
|
725 |
+ |
# layer, so it pays nothing to keep. What is left in each number is the
|
|
726 |
+ |
# ~34 MB rpmdb rewrite, which is the floor every dnf layer pays and the
|
|
727 |
+ |
# largest unattributed cost in the image. docs/IMAGE.md#size has it.
|
|
728 |
+ |
#
|
|
729 |
+ |
# One new line in the build log comes with this and is not a fault. The next
|
|
730 |
+ |
# dnf run now meets a cold cache, so it prints
|
|
731 |
+ |
# >>> repomd.xml GPG signature verification error: Signing key not found
|
|
732 |
+ |
# for Terra and Tailscale before importing the key from the RPM-GPG-KEY-*
|
|
733 |
+ |
# files terra-release itself installed, and then proceeds. It was invisible
|
|
734 |
+ |
# before only because the cache the clean now removes was still warm.
|
| 710 |
735 |
|
RUN dnf install -y --nogpgcheck \
|
| 711 |
736 |
|
--repofrompath='terra,https://repos.fyralabs.com/terra$releasever' \
|
| 712 |
|
- |
terra-release
|
|
737 |
+ |
terra-release \
|
|
738 |
+ |
&& dnf clean all
|
| 713 |
739 |
|
|
| 714 |
740 |
|
# dnf5's copr plugin isn't in the base bootc image; pull it so we
|
| 715 |
741 |
|
# can `dnf copr enable` for the Rust-Wayland stragglers.
|
| 716 |
|
- |
RUN dnf install -y 'dnf5-command(copr)'
|
|
742 |
+ |
RUN dnf install -y 'dnf5-command(copr)' && dnf clean all
|
| 717 |
743 |
|
|
| 718 |
744 |
|
# ublue-os/staging is the community-maintained COPR that packages
|
| 719 |
745 |
|
# much of the Wayland / Rust ecosystem for atomic Fedora derivatives.
|
| 1222 |
1248 |
|
# was installed without it, so both watchers failed at every login
|
| 1223 |
1249 |
|
# and the clipboard history could never be populated or pasted from.
|
| 1224 |
1250 |
|
wl-clipboard \
|
| 1225 |
|
- |
&& dnf clean all; \
|
|
1251 |
+ |
&& dnf clean all \
|
|
1252 |
+ |
# bibata-cursor-theme installs FOURTEEN directories and Alloy names one.
|
|
1253 |
+ |
# At 179 MB it was the third-largest package in the image, behind the
|
|
1254 |
+ |
# browser and helix's parsers, and ahead of rustc. Fedora ships no
|
|
1255 |
+ |
# per-variant subpackage, so a prune is the only lever.
|
|
1256 |
+ |
#
|
|
1257 |
+ |
# Measured in the built image: Bibata-Modern-{Amber,Classic,Ice} at 27 MB
|
|
1258 |
+ |
# each, Bibata-Original-{Amber,Classic,Ice} at 12 MB, and a -Right variant
|
|
1259 |
+ |
# of all six at 11-12 MB. Keeping Bibata-Modern-Classic drops 159 MB.
|
|
1260 |
+ |
#
|
|
1261 |
+ |
# It runs INSIDE this RUN on purpose. Deleting them in a later layer
|
|
1262 |
+ |
# writes whiteouts and reclaims nothing from the image, the same trap the
|
|
1263 |
+ |
# terra metadata was in.
|
|
1264 |
+ |
#
|
|
1265 |
+ |
# KEEP is spelled here rather than read from /etc/skel, which is copied in
|
|
1266 |
+ |
# further down and does not exist yet. The rename this cannot see is caught
|
|
1267 |
+ |
# at the other end instead: the assertion after the skel copy reads the
|
|
1268 |
+ |
# theme name back out of skel and fails if its directory is not here.
|
|
1269 |
+ |
&& KEEP=Bibata-Modern-Classic \
|
|
1270 |
+ |
&& test -d "/usr/share/icons/$KEEP" \
|
|
1271 |
+ |
&& for theme in /usr/share/icons/Bibata-*; do \
|
|
1272 |
+ |
[ "$theme" = "/usr/share/icons/$KEEP" ] || rm -rf "$theme"; \
|
|
1273 |
+ |
done \
|
|
1274 |
+ |
&& test -d "/usr/share/icons/$KEEP"; \
|
| 1226 |
1275 |
|
else \
|
| 1227 |
1276 |
|
# The asserting else. Five sentinels, one per reason a package could
|
| 1228 |
1277 |
|
# have landed here anyway: sway is a direct name in the `then` branch,
|
| 1638 |
1687 |
|
COPY usr/ /usr/
|
| 1639 |
1688 |
|
COPY --from=rust-build /staged-skel/ /
|
| 1640 |
1689 |
|
|
|
1690 |
+ |
# =====================================================================
|
|
1691 |
+ |
# The cursor theme skel names has to be the one the prune kept.
|
|
1692 |
+ |
# =====================================================================
|
|
1693 |
+ |
# bibata-cursor-theme ships fourteen themes and the package block above
|
|
1694 |
+ |
# deletes thirteen of them, 159 MB, keeping the one string spelled there.
|
|
1695 |
+ |
# That prune runs 600 lines before skel exists, so it cannot read the name
|
|
1696 |
+ |
# it is keeping; this is the other end of that, and it is the only thing
|
|
1697 |
+ |
# standing between a rename and a session that starts with the X11 default
|
|
1698 |
+ |
# cursor because its theme directory was thrown away at build time.
|
|
1699 |
+ |
#
|
|
1700 |
+ |
# Read from skel rather than compared against a constant. Five files declare
|
|
1701 |
+ |
# the cursor and each has its own syntax, so a rename that reaches four of
|
|
1702 |
+ |
# them and not the fifth is the realistic failure, and it is one this catches
|
|
1703 |
+ |
# where a single hardcoded name would not.
|
|
1704 |
+ |
#
|
|
1705 |
+ |
# The second loop is the mirror: every theme still on disk must be one skel
|
|
1706 |
+ |
# asked for. It fails if the prune silently stopped running, which is the
|
|
1707 |
+ |
# case that costs 159 MB rather than a broken cursor, and which nothing else
|
|
1708 |
+ |
# here would notice.
|
|
1709 |
+ |
RUN set -eu; \
|
|
1710 |
+ |
if [ "$PROFILE" = client ]; then \
|
|
1711 |
+ |
names="$( { \
|
|
1712 |
+ |
sed -n 's/^[[:space:]]*Inherits[[:space:]]*=[[:space:]]*//p' \
|
|
1713 |
+ |
/etc/skel/.icons/default/index.theme; \
|
|
1714 |
+ |
sed -n 's/^[[:space:]]*gtk-cursor-theme-name[[:space:]]*=[[:space:]]*//p' \
|
|
1715 |
+ |
/etc/skel/.config/gtk-3.0/settings.ini \
|
|
1716 |
+ |
/etc/skel/.config/gtk-4.0/settings.ini; \
|
|
1717 |
+ |
sed -n 's/^[[:space:]]*seat[[:space:]]\+[^[:space:]]\+[[:space:]]\+xcursor_theme[[:space:]]\+\([^[:space:]]\+\).*/\1/p' \
|
|
1718 |
+ |
/etc/skel/.config/sway/config; \
|
|
1719 |
+ |
sed -n 's/^.*XCURSOR_THEME[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' \
|
|
1720 |
+ |
/etc/skel/.config/nushell/env.nu; \
|
|
1721 |
+ |
} | sed 's/[[:space:]]*$//' | sort -u )"; \
|
|
1722 |
+ |
[ -n "$names" ] \
|
|
1723 |
+ |
|| { echo "no cursor theme is declared anywhere in /etc/skel; the five declarations moved and the prune in the package block is now keeping a directory nothing asks for" >&2; exit 1; }; \
|
|
1724 |
+ |
for n in $names; do \
|
|
1725 |
+ |
test -d "/usr/share/icons/$n" \
|
|
1726 |
+ |
|| { echo "skel asks for cursor theme '$n' and /usr/share/icons/$n is not in the image; the prune in the package block kept a different name" >&2; exit 1; }; \
|
|
1727 |
+ |
done; \
|
|
1728 |
+ |
for theme in /usr/share/icons/Bibata-*; do \
|
|
1729 |
+ |
[ -d "$theme" ] || continue; \
|
|
1730 |
+ |
echo "$names" | grep -qx "$(basename "$theme")" \
|
|
1731 |
+ |
|| { echo "$(basename "$theme") survived and skel does not name it; the prune in the package block stopped running and the image is carrying ~12-27 MB per unused theme" >&2; exit 1; }; \
|
|
1732 |
+ |
done; \
|
|
1733 |
+ |
echo "cursor: $(echo "$names" | tr '\n' ' ')kept, every other Bibata theme pruned"; \
|
|
1734 |
+ |
fi
|
|
1735 |
+ |
|
| 1641 |
1736 |
|
# =====================================================================
|
| 1642 |
1737 |
|
# The installer's ssh door — assert both halves of the gate are intact.
|
| 1643 |
1738 |
|
# =====================================================================
|