| 717 |
717 |
|
esac; \
|
| 718 |
718 |
|
echo "building profile=$PROFILE browser=$BROWSER langs=$LANGS trim=$TRIM"
|
| 719 |
719 |
|
|
|
720 |
+ |
# =====================================================================
|
|
721 |
+ |
# Install-time filtering: translations and documentation
|
|
722 |
+ |
# =====================================================================
|
|
723 |
+ |
# 279 MB of a naive build is files rpm has marked with a language and 101 MB
|
|
724 |
+ |
# is documentation. Two rpm settings drop most of it, and they only bind
|
|
725 |
+ |
# packages installed *after* they are set, which is why they are here — above
|
|
726 |
+ |
# terra-release, above every `dnf install` in this stage — rather than
|
|
727 |
+ |
# somewhere more convenient. Set late they silently do almost nothing and the
|
|
728 |
+ |
# build still passes, which is the failure mode the assertion after the last
|
|
729 |
+ |
# install exists to catch.
|
|
730 |
+ |
#
|
|
731 |
+ |
# /etc/rpm/macros.image-language-conf %_install_langs en:en_US
|
|
732 |
+ |
# /etc/dnf/dnf.conf tsflags=nodocs
|
|
733 |
+ |
#
|
|
734 |
+ |
# MEASURED, not argued (2026-08-17): gtk3, gtk4, helix and nushell built both
|
|
735 |
+ |
# ways. `/usr/share/locale` grew 112 MB without these and 0.5 MB with them.
|
|
736 |
+ |
# Alloy's own share of the cut is about 170 MB of translations plus about
|
|
737 |
+ |
# 92 MB of documentation. The ~160 MB the base carries in before this line
|
|
738 |
+ |
# runs is out of reach and stays. This is what actually makes Alpine images
|
|
739 |
+ |
# small — it is not musl, and it works on glibc. Full lever list and the
|
|
740 |
+ |
# figures behind it: wiki `alloy-image-size-levers`.
|
|
741 |
+ |
#
|
|
742 |
+ |
# Install-time filtering rather than a prune, and the difference is worth
|
|
743 |
+ |
# stating: the files are never installed and the rpm database knows it, so
|
|
744 |
+ |
# `rpm -V` still agrees with the image. Compare the cursor prune, where the
|
|
745 |
+ |
# database declares 188 MB against 27 MB on disk by design. Prefer this shape
|
|
746 |
+ |
# wherever both would work.
|
|
747 |
+ |
#
|
|
748 |
+ |
# THE MAN PAGE LOSS IS DECIDED, NOT OVERLOOKED. Fedora marks man pages %doc
|
|
749 |
+ |
# and rpm offers no separate switch, so `nodocs` takes them with it. A default
|
|
750 |
+ |
# Alloy install has no `man` for anything installed after this line; the
|
|
751 |
+ |
# console carries its own help and docs/manual exists, so the loss is for
|
|
752 |
+ |
# third-party tools rather than for Alloy's own surface. `TRIM=keep` restores
|
|
753 |
+ |
# them. Ruled by Max 2026-08-17 over the recommendation, which was the
|
|
754 |
+ |
# language macro alone.
|
|
755 |
+ |
#
|
|
756 |
+ |
# LICENCES ARE NOT AT RISK, checked both ways: rpm treats %license separately
|
|
757 |
+ |
# from %doc, `/usr/share/licenses` stayed 12 MB and gtk4's licence stayed
|
|
758 |
+ |
# readable. The assertion after the last install proves it anyway, because the
|
|
759 |
+ |
# credits manifest is hand-curated against what the image ships and a future
|
|
760 |
+ |
# rpm change here would be a licensing problem rather than a size one.
|
|
761 |
+ |
#
|
|
762 |
+ |
# It rides TRIM rather than taking an axis of its own. TRIM already means
|
|
763 |
+ |
# "remove what this machine cannot use" and defaults to `unused`, so this is
|
|
764 |
+ |
# the same argument one layer down and the builder matrix stays
|
|
765 |
+ |
# profile x browser x langs x trim. Rejected: a LOCALES arg, which is the
|
|
766 |
+ |
# fourth axis wiki `alloy-distribution` asks nobody to add, and doing it
|
|
767 |
+ |
# unconditionally, which leaves no way back for a build-it-yourself consumer
|
|
768 |
+ |
# who wants documentation.
|
|
769 |
+ |
#
|
|
770 |
+ |
# The baseline written here is what that assertion compares against: the
|
|
771 |
+ |
# base's own `/usr/share/locale`, read at build time rather than hardcoded,
|
|
772 |
+ |
# since it moves when the FROM line does. The assertion deletes it.
|
|
773 |
+ |
RUN set -eu; \
|
|
774 |
+ |
mkdir -p /usr/lib/alloy /etc/rpm; \
|
|
775 |
+ |
du -sb /usr/share/locale | cut -f1 > /usr/lib/alloy/.locale-baseline; \
|
|
776 |
+ |
[ -s /usr/lib/alloy/.locale-baseline ] \
|
|
777 |
+ |
|| { echo "could not read the base's /usr/share/locale size; the assertion below would have nothing to compare against" >&2; exit 1; }; \
|
|
778 |
+ |
grep -q '^\[main\]' /etc/dnf/dnf.conf \
|
|
779 |
+ |
|| { echo "/etc/dnf/dnf.conf has no [main] section; tsflags would land outside every section and do nothing" >&2; exit 1; }; \
|
|
780 |
+ |
if [ "$TRIM" = unused ]; then \
|
|
781 |
+ |
echo '%_install_langs en:en_US' > /etc/rpm/macros.image-language-conf; \
|
|
782 |
+ |
sed -i '/^\[main\]/a tsflags=nodocs' /etc/dnf/dnf.conf; \
|
|
783 |
+ |
grep -q '^tsflags=nodocs$' /etc/dnf/dnf.conf \
|
|
784 |
+ |
|| { echo "tsflags=nodocs did not land in /etc/dnf/dnf.conf" >&2; exit 1; }; \
|
|
785 |
+ |
echo "langs: en:en_US only, and no documentation or man pages (rides TRIM=unused)"; \
|
|
786 |
+ |
else \
|
|
787 |
+ |
rm -f /etc/rpm/macros.image-language-conf; \
|
|
788 |
+ |
echo "langs: every language and all documentation (TRIM=keep)"; \
|
|
789 |
+ |
fi
|
|
790 |
+ |
|
| 720 |
791 |
|
# =====================================================================
|
| 721 |
792 |
|
# Third-party repos
|
| 722 |
793 |
|
# =====================================================================
|
| 1628 |
1699 |
|
|| { echo "$fw is not in this image; a medium built here cannot drive hardware nobody asked about at build time, and the trim must never reach firmware" >&2; exit 1; }; \
|
| 1629 |
1700 |
|
done
|
| 1630 |
1701 |
|
|
|
1702 |
+ |
# The language and documentation macros, proved to have taken. They are set
|
|
1703 |
+ |
# hundreds of lines above and bind only what is installed after them, so a
|
|
1704 |
+ |
# silent no-op here looks exactly like success: the build passes, the image is
|
|
1705 |
+ |
# a quarter of a gigabyte bigger than it should be, and nobody notices for a
|
|
1706 |
+ |
# year. This runs after the last `dnf install` in the stage for that reason.
|
|
1707 |
+ |
#
|
|
1708 |
+ |
# The gate is `/usr/share/locale` against the baseline the macro layer
|
|
1709 |
+ |
# recorded, plus a wide margin. Four packages alone grew it by 112 MB with the
|
|
1710 |
+ |
# macros off and by 0.5 MB with them on, so 32 MiB of headroom is unreachable
|
|
1711 |
+ |
# by a correct build and unmissable by a broken one. Reading the baseline at
|
|
1712 |
+ |
# build time rather than hardcoding it keeps this honest when the FROM line
|
|
1713 |
+ |
# moves and the base's own contribution with it.
|
|
1714 |
+ |
#
|
|
1715 |
+ |
# Both branches assert, the same way the trim above does. `keep` proves the
|
|
1716 |
+ |
# macro file is really absent rather than trusting that the conditional took
|
|
1717 |
+ |
# the branch it was asked for.
|
|
1718 |
+ |
#
|
|
1719 |
+ |
# Licences are checked on both branches, for the reason given at the macro
|
|
1720 |
+ |
# layer: rpm treats %license separately from %doc today, and an upstream
|
|
1721 |
+ |
# change that swept them together would be a licensing problem rather than a
|
|
1722 |
+ |
# size one.
|
|
1723 |
+ |
RUN set -eu; \
|
|
1724 |
+ |
baseline="$(cat /usr/lib/alloy/.locale-baseline)"; \
|
|
1725 |
+ |
rm -f /usr/lib/alloy/.locale-baseline; \
|
|
1726 |
+ |
now="$(du -sb /usr/share/locale | cut -f1)"; \
|
|
1727 |
+ |
grown=$(( now - baseline )); \
|
|
1728 |
+ |
[ -n "$(ls -A /usr/share/licenses 2>/dev/null)" ] \
|
|
1729 |
+ |
|| { echo "/usr/share/licenses is empty; %license was swept along with %doc and crates/alloy/credits.toml no longer describes what the image ships" >&2; exit 1; }; \
|
|
1730 |
+ |
if [ "$TRIM" = unused ]; then \
|
|
1731 |
+ |
[ "$grown" -le 33554432 ] \
|
|
1732 |
+ |
|| { echo "/usr/share/locale grew $((grown / 1048576)) MB over the base, so the language macro did not take. The likely cause is a dnf install that moved above the layer setting it." >&2; exit 1; }; \
|
|
1733 |
+ |
[ -f /etc/rpm/macros.image-language-conf ] \
|
|
1734 |
+ |
|| { echo "the language macro file is gone; something below the macro layer removed it" >&2; exit 1; }; \
|
|
1735 |
+ |
grep -q '^tsflags=nodocs$' /etc/dnf/dnf.conf \
|
|
1736 |
+ |
|| { echo "tsflags=nodocs is no longer in /etc/dnf/dnf.conf; documentation was installed from wherever it was dropped onward" >&2; exit 1; }; \
|
|
1737 |
+ |
else \
|
|
1738 |
+ |
[ ! -f /etc/rpm/macros.image-language-conf ] \
|
|
1739 |
+ |
|| { echo "TRIM=keep but the language macro is set; the macro layer ran the wrong branch" >&2; exit 1; }; \
|
|
1740 |
+ |
fi; \
|
|
1741 |
+ |
echo "langs: /usr/share/locale grew $((grown / 1048576)) MB over the base"
|
|
1742 |
+ |
|
| 1631 |
1743 |
|
# =====================================================================
|
| 1632 |
1744 |
|
# System user for greetd. greetd drops privileges to this account
|
| 1633 |
1745 |
|
# before spawning tuigreet; without it greetd exits with
|