diff --git a/build.sh b/build.sh index 92cf741..393b241 100755 --- a/build.sh +++ b/build.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -euo pipefail -MOUNT="$(mktemp --directory)" - HOSTNAME="arch" KEYMAP="us" LOCALE="C.UTF-8" @@ -12,17 +10,28 @@ IMG_SIZE="2G" IMG_FILE="image.img" QCOW_FILE="image.qcow2" +# This setup makes writing fstab unnecessary because : +# - root partition is automatically mounted according to its GPT partition type +# - rootflags including subvol are set with kernel cmdline +# https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ + +ESP_GPT_TYPE="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" # EFI System ESP_LABEL="ESP" ESP_SIZE="100M" ESP_DIR="efi" -ESP_GPT_TYPE="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" # EFI System - -ROOT_LABEL="Arch Linux" +ROOT_GPT_TYPE="4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709" # Linux root (x86-64) +ROOT_LABEL="Arch" ROOT_SUBVOL="@arch" ROOT_FLAGS="compress=zstd,noatime,subvol=$ROOT_SUBVOL" -ROOT_GPT_TYPE="4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709" # Linux root (x86-64) -PACKAGES=( +BUILD_DEPENDENCIES+=( + arch-install-scripts + btrfs-progs + dosfstools + qemu-img + util-linux +) +PACKAGES+=( base btrfs-progs cloud-init @@ -31,22 +40,20 @@ PACKAGES=( grml-zsh-config htop iptables-nft - # linux + linux man-db mkinitcpio neovim openssh pacman-contrib polkit - reflector sudo systemd-ukify zsh zsh-autosuggestions - zsh-completions zsh-syntax-highlighting ) -UNITS_ENABLE=( +UNITS_ENABLE+=( cloud-init cloud-init-local cloud-config @@ -62,11 +69,15 @@ UNITS_ENABLE=( btrfs-scrub@-.timer paccache.timer ) -UNITS_DISABLE=( +UNITS_MASK+=( + systemd-homed.service + systemd-nsresourced.socket systemd-userdbd.socket ) -# Cleanup +MOUNT="$(mktemp --directory)" + +# Cleanup trap cleanup() { if findmnt --mountpoint "$MOUNT" >/dev/null; then umount --recursive "$MOUNT" @@ -78,56 +89,57 @@ cleanup() { } trap cleanup ERR -# Image setup +echo "### DEPENDENCIES" >&2 +sed -i 's/^#ParallelDownloads/ParallelDownloads/' "/etc/pacman.conf" +pacman -Syu --needed --noconfirm "${BUILD_DEPENDENCIES[@]}" + +echo "### IMAGE SETUP" >&2 rm -f $IMG_FILE truncate --size $IMG_SIZE $IMG_FILE -# Image format +echo "### PARTITIONING" >&2 +# Flag 59 marks the partition for automatic growing of the contained file system +# https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ sfdisk --label gpt $IMG_FILE <&2 LOOPDEV=$(losetup --find --partscan --show $IMG_FILE) +PART1="$LOOPDEV"p1 +PART2="$LOOPDEV"p2 sleep 1 -mkfs.vfat -F 32 -n "$ESP_LABEL" "$LOOPDEV"p1 -mkfs.btrfs -L "$ROOT_LABEL" "$LOOPDEV"p2 +echo "### FORMATTING" >&2 +mkfs.vfat -F 32 -n "$ESP_LABEL" "$PART1" +mkfs.btrfs --label "$ROOT_LABEL" "$PART2" -# Image mount -mount "$LOOPDEV"p2 "$MOUNT" +echo "### MOUNTING" >&2 +mount "$PART2" "$MOUNT" btrfs subvolume create "$MOUNT/$ROOT_SUBVOL" btrfs subvolume set-default "$MOUNT/$ROOT_SUBVOL" umount "$MOUNT" -mount -o "$ROOT_FLAGS" "$LOOPDEV"p2 "$MOUNT" -mount --mkdir=700 "$LOOPDEV"p1 "$MOUNT/$ESP_DIR" +mount --options "$ROOT_FLAGS" "$PART2" "$MOUNT" +mount --mkdir=700 "$PART1" "$MOUNT/$ESP_DIR" -# Install -pacstrap -cGM "$MOUNT" "${PACKAGES[@]}" - -# Setting fstab is unnecessary for the following reasons: -# root partition is automatically mounted with its GPT partition type -# root partition grows thanks to GPT flag 59 set with sfdisk earlier https://github.com/systemd/systemd/pull/30030 -# subvol is implicit from `btrfs subvolume set-default` and set with cmdline anyway -# compress & noatime are set by cmdline +echo "### KERNEL CMDLINE CONFIG" >&2 # Not specifying `rw` in cmdline breaks boot -CMDLINE="rootflags=$ROOT_FLAGS rw" -systemd-firstboot \ - --root="$MOUNT" \ - --force \ - --keymap="$KEYMAP" \ - --locale="$LOCALE" \ - --hostname="$HOSTNAME" \ - --timezone="$TIMEZONE" \ - --root-shell=/usr/bin/zsh \ - --kernel-command-line="$CMDLINE" \ - ; +mkdir --parents "$MOUNT/etc/kernel" +cat <"$MOUNT/etc/kernel/cmdline" +rootflags=$ROOT_FLAGS rw +EOF -# Bootloader -bootctl install --root "$MOUNT" --no-variables +echo "### INITRD CONFIG" >&2 +mkdir --parents "$MOUNT/etc/mkinitcpio.conf.d" cat <"$MOUNT/etc/mkinitcpio.conf.d/custom.conf" MODULES=(btrfs) HOOKS=(systemd autodetect microcode modconf keyboard block) EOF + +echo "### UKI CONFIG" >&2 +mkdir --parents "$MOUNT/etc/mkinitcpio.d" +mkdir --parents "$MOUNT/$ESP_DIR/EFI/Linux/" cat <"$MOUNT/etc/mkinitcpio.d/linux.preset" PRESETS=('default') default_kver="/boot/vmlinuz-linux" @@ -135,23 +147,26 @@ default_uki="/$ESP_DIR/EFI/Linux/arch.efi" default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp -S autodetect" EOF -# Kernel -pacstrap -cGM "$MOUNT" linux +echo "### PACSTRAP" >&2 +pacstrap -cGM "$MOUNT" "${PACKAGES[@]}" sed -i "s/ -S autodetect//" "$MOUNT/etc/mkinitcpio.d/linux.preset" -# https://systemd.io/BUILDING_IMAGES/ -rm -f "$MOUNT/etc/machine-id" -rm -f "$MOUNT/var/lib/systemd/random-seed" -rm -f "$MOUNT/$ESP_DIR/loader/random-seed" +echo "### BOOTLOADER INSTALL" >&2 +bootctl install --root "$MOUNT" --no-variables -# Use systemd-repart to grow the root partition -mkdir "$MOUNT/etc/repart.d" -cat <"$MOUNT/etc/repart.d/root.conf" -[Partition] -Type=root -EOF +echo "### FIRSTBOOT SETTINGS" >&2 +systemd-firstboot \ + --root="$MOUNT" \ + --force \ + --keymap="$KEYMAP" \ + --locale="$LOCALE" \ + --hostname="$HOSTNAME" \ + --timezone="$TIMEZONE" \ + --root-shell=/usr/bin/zsh \ + ; -# Basic Network DHCP Setup +echo "### NETWORK SETTINGS" >&2 +ln -sf /run/systemd/resolve/stub-resolv.conf "$MOUNT/etc/resolv.conf" cat <"$MOUNT/etc/systemd/network/99-ethernet.network" [Match] Name=en* @@ -160,8 +175,18 @@ Type=ether [Network] DHCP=yes EOF -ln -sf /run/systemd/resolve/stub-resolv.conf "$MOUNT/etc/resolv.conf" +echo "### CLOUD IMAGE SETTINGS" >&2 +# https://systemd.io/BUILDING_IMAGES/ +rm -f "$MOUNT/etc/machine-id" +rm -f "$MOUNT/var/lib/systemd/random-seed" +rm -f "$MOUNT/$ESP_DIR/loader/random-seed" +# Use systemd-repart to grow the root partition +mkdir --parents "$MOUNT/etc/repart.d" +cat <"$MOUNT/etc/repart.d/root.conf" +[Partition] +Type=root +EOF # Pacman Keyring Initialization cat <"$MOUNT/etc/systemd/system/pacman-init.service" [Unit] @@ -179,7 +204,6 @@ ExecStart=/usr/bin/pacman-key --populate [Install] WantedBy=multi-user.target EOF - # Cloud Init Settings cat <"$MOUNT/etc/cloud/cloud.cfg.d/custom.cfg" system_info: @@ -195,40 +219,43 @@ disable_root: true disable_root_opts: "#" EOF -# Services -systemctl --root="$MOUNT" enable "${UNITS_ENABLE[@]}" -systemctl --root="$MOUNT" disable "${UNITS_DISABLE[@]}" - +echo "### MISC SETTINGS" >&2 # Pacman config sed -i 's/^#Color/Color/' "$MOUNT/etc/pacman.conf" sed -i 's/^#ParallelDownloads/ParallelDownloads/' "$MOUNT/etc/pacman.conf" sed -i 's/^#VerbosePkgLists/VerbosePkgLists/' "$MOUNT/etc/pacman.conf" - # Mirror list cat <"$MOUNT/etc/pacman.d/mirrorlist" Server = https://geo.mirror.pkgbuild.com/\$repo/os/\$arch EOF - # Disable SSH password and root login cat <"$MOUNT/etc/ssh/sshd_config.d/custom.conf" PermitRootLogin no PasswordAuthentication no EOF - # ZSH plugins cat <>"$MOUNT/etc/zsh/zshrc" source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh source <(fzf --zsh) EOF - # Neovim Symlinks -ln -sf /usr/bin/nvim "$MOUNT/usr/local/bin/vim" -ln -sf /usr/bin/nvim "$MOUNT/usr/local/bin/vi" +ln -s /usr/bin/nvim "$MOUNT/usr/local/bin/vim" +ln -s /usr/bin/nvim "$MOUNT/usr/local/bin/vi" + +echo "### ENABLE UNITS" >&2 +systemctl --root="$MOUNT" enable "${UNITS_ENABLE[@]}" + +echo "### MASK UNITS" >&2 +systemctl --root="$MOUNT" mask "${UNITS_MASK[@]}" -# Image cleanup +echo "### CLEANUP" >&2 sync -f "$MOUNT/etc/os-release" fstrim --verbose "$MOUNT/$ESP_DIR" fstrim --verbose "$MOUNT" cleanup + +echo "### CREATE QCOW2" >&2 qemu-img convert -f raw -O qcow2 "$IMG_FILE" "$QCOW_FILE" + +echo "### FINISHED WITHOUT ERRORS" >&2