diff --git a/.gitignore b/.gitignore index 2c1b1b4..0c5d44e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ destroot repo +mnt +README.html +pandoc.css diff --git a/.gitmodules b/.gitmodules index 1164564..aa494ad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "modules/aports"] path = modules/aports url = https://git.alpinelinux.org/aports/ +[submodule "modules/bash-util"] + path = modules/bash-util + url = https://github.com/targetdisk/bash-util diff --git a/GNUmakefile b/GNUmakefile index 68f237f..722c3f9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,5 +1,5 @@ ARCH ?= x86_64 -PLATFORM ?= UEFI +PLATFORM ?= uefi DESTROOT ?= destroot REPO ?= repo DOSU ?= sudo @@ -8,6 +8,52 @@ PKGS ?= BUSYBOX ?= busybox APORTSDIR ?= $(APORTS_MODULE) SHELL := /usr/bin/env bash +BLKDEV ?= +ROOTFS_TYPE ?= btrfs +MOUNTPOINT ?= mnt + +RUNLEVEL_SYSINIT ?= devfs dmesg mdev hwdrivers +RUNLEVEL_SHUTDOWN ?= killprocs mount-ro savecache + +ifeq ($(PLATFORM),raspi) + include mk/raspi.mk +else ifeq ($(PLATFORM),uefi) + include mk/uefi.mk +endif + +ifeq ($(ROOTFS_TYPE),btrfs) + include mk/btrfs.mk +else + include mk/simplefs.mk +endif + +ifneq (,$(WAYLAND_DISPLAY)) + XDG_BASEDIR := $(shell dirname $(XDG_RUNTIME_DIR)) + WAYLAND_ASSIST := env WAYLAND_DISPLAY="$(XDG_RUNTIME_DIR)/$(WAYLAND_DISPLAY)" \ + XDG_BASEDIR="$(XDG_BASEDIR)/0" +else + WAYLAND_ASSIST := +endif + +RUNLEVEL_BOOT_CMDS := $(foreach SERVICE,$(RUNLEVEL_BOOT),\ + rc-update add $(SERVICE) boot &&) +RUNLEVEL_SYSINIT_CMDS := $(foreach SERVICE,$(RUNLEVEL_SYSINIT),\ + rc-update add $(SERVICE) sysinit &&) +RUNLEVEL_DEFAULT_CMDS := $(foreach SERVICE,$(RUNLEVEL_DEFAULT),\ + rc-update add $(SERVICE) default &&) +RUNLEVEL_SHUTDOWN_CMDS := $(foreach SERVICE,$(RUNLEVEL_SHUTDOWN),\ + rc-update add $(SERVICE) shutdown &&) + +RUNLEVEL_CMDS := $(RUNLEVEL_BOOT_CMDS) $(RUNLEVEL_SYSINIT_CMDS) \ + $(RUNLEVEL_DEFAULT_CMDS) $(RUNLEVEL_SHUTDOWN_CMDS) : + +ifneq (,$(findstring /dev/mmcblk,$(BLKDEV))) + P := p +else ifneq (,$(findstring /dev/nvme,$(BLKDEV))) + P := p +else + P := +endif GIMME_MODULE := git submodule update --init --recursive @@ -15,13 +61,20 @@ ABUILD_MODULE := modules/abuild ARCH_MODULE := modules/arch-install-scripts APK_MODULE := modules/apk-tools APORTS_MODULE := modules/aports +BASH_UTIL_MODULE := modules/bash-util ABUILD_SCRIPT := $(ABUILD_MODULE)/abuild APK_BIN := $(APK_MODULE)/src/apk ABUILD := $(BUSYBOX) ash $(CURDIR)/$(ABUILD_SCRIPT) -c -P $(CURDIR)/$(REPO) rootbld ACHROOT := $(ARCH_MODULE)/arch-chroot +GENFSTAB := $(ARCH_MODULE)/genfstab +ACHROOT_CMD := $(DOSU) $(ACHROOT) $(DESTROOT) /bin/sh -c '. /etc/profile' +ACHROOTI_CMD := $(DOSU) $(ACHROOT) $(MOUNTPOINT) /bin/sh -c '. /etc/profile' APK := LD_LIBRARY_PATH=$(APK_MODULE)/src $(APK_BIN) +BUTIL := . $(BASH_UTIL_MODULE) + +default-target: install ### TOOLS ### @@ -33,6 +86,9 @@ $(ARCH_MODULE)/Makefile: .gitmodules $(ACHROOT): $(ARCH_MODULE)/Makefile $(MAKE) -j -C $(ARCH_MODULE) arch-chroot +$(GENFSTAB): $(ARCH_MODULE)/Makefile + $(MAKE) -j -C $(ARCH_MODULE) genfstab + $(APK_MODULE)/Makefile: .gitmodules $(GIMME_MODULE) -- $(APK_MODULE) @@ -45,6 +101,9 @@ $(ABUILD_MODULE)/Makefile: .gitmodules $(ABUILD_SCRIPT): $(ABUILD_MODULE)/Makefile $(MAKE) -j -C $(ABUILD_MODULE) all +$(BASH_UTIL_MODULE)/Makefile: .gitmodules + $(GIMME_MODULE) -- $(BASH_UTIL_MODULE) + ### PORTS TREE ### $(APORTS_MODULE)/README.md: .gitmodules @@ -56,25 +115,107 @@ aports: $(APORTS_MODULE)/README.md # Something will go here... -### INSTALLATION ### +### BOOTSTRAPPING ### $(DESTROOT): mkdir -p $(DESTROOT) || $(DOSU) mkdir -p $(DESTROOT) $(DESTROOT)/.bootstrap-done: $(DESTROOT) $(APK_BIN) $(DOSU) $(APK) --arch $(ARCH) -X $(ALPINE_REPO) --root $(DESTROOT) \ - -U --allow-untrusted --initdb add alpine-keys $(PKGS) && touch $@ + -U --allow-untrusted --initdb add alpine-keys && touch $@ $(DOSU) $(APK) --arch $(ARCH) -X $(ALPINE_REPO) --root $(DESTROOT) \ - add alpine-base $(PKGS) && touch $@ + add alpine-base btrfs-progs e2fsprogs $(PLATFORM_PKGS) $(PKGS) \ + && touch $@ bootstrap: $(DESTROOT)/.bootstrap-done +### PARTITIONING ### + +blkcheck: $(BASH_UTIL_MODULE)/Makefile + @[ -z "$(BLKDEV)" ] && $(BUTIL)/logging.bash \ + && die 'ERROR: Must define BLKDEV env variable!' \ + || : + +format: partition + $(DOSU) mkfs.vfat $(BLKDEV)$(P)1 + $(DOSU) mkfs.$(ROOTFS_TYPE) -f $(BLKDEV)$(P)2 + +$(BLKDEV)$(P)2: partition + +### INSTALLATION ### + +$(DESTROOT)/.services-done: $(DESTROOT)/.bootstrap-done $(ACHROOT) + $(ACHROOT_CMD)' && $(RUNLEVEL_CMDS)' + $(DOSU) touch $@ + +services: $(DESTROOT)/.services-done + +$(DESTROOT)/.fastrepo-done: $(DESTROOT)/.bootstrap-done $(ACHROOT) + $(ACHROOT_CMD)' && setup-apkrepos -f' + $(DOSU) touch $@ + +fastest-repo: $(DESTROOT)/.fastrepo-done + +$(MOUNTPOINT): + mkdir -p $@ || $(DOSU) mkdir -p $@ + +$(MOUNTPOINT)/etc/fstab: $(GENFSTAB) mount + $(GENFSTAB) -U $(MOUNTPOINT) | $(DOSU) tee $@ + +fstab: $(MOUNTPOINT)/etc/fstab + +mount: $(MOUNTPOINT)/.mount-done + +$(MOUNTPOINT)/.install-done: fstab services fastest-repo + @[ -z "$(MOUNTPOINT)" ] && $(BUTIL)/logging.bash \ + && die 'ERROR: Must define MOUNTPOINT env variable!' \ + || : + $(DOSU) rsync -aAXHv \ + --exclude=$(DESTROOT)'/dev/*' \ + --exclude=$(DESTROOT)'/proc/*' \ + --exclude=$(DESTROOT)'/sys/*' \ + --exclude=$(DESTROOT)'/tmp/*' \ + --exclude=$(DESTROOT)'/run/*' \ + --exclude=$(DESTROOT)'/mnt/*' \ + --exclude=$(DESTROOT)'/media/*' \ + --exclude=$(DESTROOT)'/lost+found/' \ + $(DESTROOT)/ $(MOUNTPOINT) + $(DOSU) touch $@ + +install: $(MOUNTPOINT)/.install-done + +bootloader: $(MOUNTPOINT)/.bootloader-done + +### POST-INSTALL TOOLS ### + +# Launch a shell into the installed volume +install-chroot: $(MOUNTPOINT)/.install-done $(ACHROOT) + $(ACHROOTI_CMD)' && sh' + +# Launch a shell into the local bootstrapped root +chroot: $(DESTROOT)/.bootstrap-done $(ACHROOT) + $(ACHROOT_CMD)' && sh' + ### CLEANLINESS ### -clean: +clean: umount git submodule deinit -f -- \ - $(APK_MODULE) $(ABUILD_MODULE) $(APORTS_MODULE) $(ARCH_MODULE) + $(APK_MODULE) $(ABUILD_MODULE) $(APORTS_MODULE) $(ARCH_MODULE) \ + $(BASH_UTIL_MODULE) $(DOSU) rm -rf destroot/* repo/* ### MAKEY-MAKEY ### -.PHONY: tools bootstrap aports +.PHONY: default-target tools bootstrap aports blkcheck format install \ + $(PLATFORM_PHONYS) mount fstab chroot + +### DOCS ################ Require Pandoc to be installed ############## DOCS ### + +pandoc.css: + wget https://sqt.wtf/~targetdisk/pandoc.css + +# Requires Pandoc to be installed +README.html: README.md pandoc.css + pandoc $< -s -c pandoc.css -o $@ + +README: README.html + xdg-open $< diff --git a/README.md b/README.md index 14fc2d4..9e4e6dc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Alpstrap -These are my scripts for brining up an Alpine system! +These are my scripts for bringing up an Alpine system! ## DEPENDENCIES - A sane shell like GNU Bash @@ -8,10 +8,67 @@ These are my scripts for brining up an Alpine system! - GNU `install` (from coreutils) - M4 - GNU Autoconf +- `qemu-user-static` and its `binfmt` rules (for cross-chrooting) You may need to install some Lua dependencies for building `apk`. If you feel you've already installed them and it's still failing, double-check the Lua version that `apk`'s build requires! +## USING + +### TL;DR, where's the cheat sheet? +Set your `PLATFORM`, `ARCH`, and `BLKDEV` to bootstrap to: +```bash +export PLATFORM=raspi ARCH=aarch64 BLKDEV=/dev/sdb +``` + +>

Note

+> +> I like to use the `lsblk` command when locating a `BLKDEV`. + +If your system has `doas` instead of `sudo`, be sure to also `export` +an appropriate `DOSU` environment variable for `make` to use: +```bash +export DOSU=doas +``` + +Format your `BLKDEV`: +```bash +make format +``` + +Then, install to the `BLKDEV` of your choosing: +```bash +make install +``` + +### Bootstrapping the base Alpine system +The Alpstrap system first bootstraps the base Alpine system to a `DESTROOT` +directory (an automatically-created `destroot` directory in this repository's +root) for maximum speed. + +The system supports bootstrapping to +[any architecture supported by Alpine](https://dl-cdn.alpinelinux.org/alpine/latest-stable/main/) +and will support *even more* in the future once rebuilding the Alpine ports tree +is automated! All you have to do is specify your desired `PLATFORM` and `ARCH` +on the GNU `make` command line as environment variables. The currently +supported `PLATFORM`s are `uefi` (default) and `raspi`. + +If you want only to bootstrap, there's a `make` target for that: +```bash +make bootstrap +``` + +## TODO +- [ ] Add bootloader/stub installation on UEFI +- [ ] Automate cross-compiling ports tree on non-Alpine hosts with only Git + submodules. +- [ ] Add Bcachefs support +- [ ] Make self-hosted installer +- [ ] Add PXE-netbooted installer and ad-hoc TFTP server +- [ ] Finish raspi `/boot` partition. +- [ ] Share helper scripts with upstream Alpine?? + ## SEE ALSO * Alpine's [wiki page on bootstrapping](https://wiki.alpinelinux.org/wiki/Bootstrapping_Alpine_Linux) +* Alpine's [wiki page on bootloaders](https://wiki.alpinelinux.org/wiki/Bootloaders) diff --git a/mk/btrfs.mk b/mk/btrfs.mk new file mode 100644 index 0000000..73436a1 --- /dev/null +++ b/mk/btrfs.mk @@ -0,0 +1,45 @@ +FSOPTS ?= defaults,discard,ssd,noatime + +$(MOUNTPOINT)/.subvols-done: $(BLKDEV)$(P)2 $(MOUNTPOINT) + $(DOSU) mount -o $(FSOPTS) $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT) + cd $(MOUNTPOINT) && \ + $(DOSU) btrfs subvolume create alp-root-$(PLATFORM)-$(ARCH) && \ + $(DOSU) btrfs subvolume create home && \ + $(DOSU) btrfs subvolume create root && \ + cd alp-root-$(PLATFORM)-$(ARCH) && \ + $(DOSU) mkdir home root && \ + $(DOSU) btrfs subvolume create etc && \ + $(DOSU) btrfs subvolume create var && \ + $(DOSU) btrfs subvolume create usr && \ + $(DOSU) btrfs subvolume create opt + $(DOSU) umount $(MOUNTPOINT) + $(DOSU) mount -o $(FSOPTS),subvol=/alp-root-$(PLATFORM)-$(ARCH) \ + $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT) + $(DOSU) mount -o $(FSOPTS),subvol=/home \ + $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT)/home + $(DOSU) mount -o $(FSOPTS),subvol=/root \ + $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT)/root + $(DOSU) mount -o $(FSOPTS),subvol=/alp-root-$(PLATFORM)-$(ARCH)/etc \ + $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT)/etc + $(DOSU) mount -o $(FSOPTS),subvol=/alp-root-$(PLATFORM)-$(ARCH)/var \ + $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT)/var + $(DOSU) mount -o $(FSOPTS),subvol=/alp-root-$(PLATFORM)-$(ARCH)/usr \ + $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT)/usr + $(DOSU) mount -o $(FSOPTS),subvol=/alp-root-$(PLATFORM)-$(ARCH)/opt \ + $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT)/opt + $(DOSU) touch $@ + +$(MOUNTPOINT)/.mount-done: $(MOUNTPOINT)/.subvols-done + $(DOSU) mkdir -p $(MOUNTPOINT)/boot + $(DOSU) mount -o defaults,discard $(BLKDEV)$(P)$(BOOTFS_PART) $(MOUNTPOINT)/boot + $(DOSU) touch $@ + +umount: + $(DOSU) umount $(MOUNTPOINT)/boot || : + $(DOSU) umount $(MOUNTPOINT)/etc || : + $(DOSU) umount $(MOUNTPOINT)/home || : + $(DOSU) umount $(MOUNTPOINT)/opt || : + $(DOSU) umount $(MOUNTPOINT)/root || : + $(DOSU) umount $(MOUNTPOINT)/usr || : + $(DOSU) umount $(MOUNTPOINT)/var || : + $(DOSU) umount $(MOUNTPOINT) || : diff --git a/mk/raspi.mk b/mk/raspi.mk new file mode 100644 index 0000000..6d93870 --- /dev/null +++ b/mk/raspi.mk @@ -0,0 +1,21 @@ +ifeq ($(ARCH),aarch64) +else ifeq ($(ARCH),armhf) +else ifeq ($(ARCH),armv7) +else + $(error $(ARCH) is unsupported on the raspi platform) +endif + +PLATFORM_PHONYS := partition kernel +KERNEL_FLAVOR := rpi +KERNEL_PKG := linux-$(KERNEL_FLAVOR) +PLATFORM_PKGS := $(KERNEL_PKG) + +BOOTFS_PART := 1 +ROOTFS_PART := 2 + +RUNLEVEL_BOOT ?= modules bootmisc hostname networking seedrng swap +RUNLEVEL_DEFAULT ?= crond + +partition: blkcheck + $(DOSU) fdisk $(BLKDEV) <<<$$'o\nw\n' + $(DOSU) sfdisk $(BLKDEV) <<<$$',1G,c,*\n,,83,\n' diff --git a/mk/simplefs.mk b/mk/simplefs.mk new file mode 100644 index 0000000..6c2c5a7 --- /dev/null +++ b/mk/simplefs.mk @@ -0,0 +1,11 @@ +FSOPTS ?= defaults,discard,noatime + +$(MOUNTPOINT)/.mount-done: $(BLKDEV)$(P)2 $(MOUNTPOINT) + $(DOSU) mount -o $(FSOPTS) $(BLKDEV)$(P)$(ROOTFS_PART) $(MOUNTPOINT) + $(DOSU) mkdir -p $(MOUNTPOINT)/boot + $(DOSU) mount -o $(FSOPTS) $(BLKDEV)$(P)$(BOOTFS_PART) $(MOUNTPOINT)/boot + $(DOSU) touch $@ + +umount: + $(DOSU) umount $(MOUNTPOINT)/boot || : + $(DOSU) umount $(MOUNTPOINT) || : diff --git a/mk/uefi.mk b/mk/uefi.mk new file mode 100644 index 0000000..99612f6 --- /dev/null +++ b/mk/uefi.mk @@ -0,0 +1,52 @@ +PLATFORM_PHONYS := partition kernel +KERNEL_FLAVOR ?= lts +KERNEL_PKG := linux-$(KERNEL_FLAVOR) +PLATFORM_PKGS := $(KERNEL_PKG) + +BOOTFS_PART := 1 +ROOTFS_PART := 2 + +RUNLEVEL_BOOT ?= hwclock modules bootmisc hostname networking seedrng swap +RUNLEVEL_DEFAULT ?= acpid crond + +ifeq ($(ARCH),x86_64) + OVMF_ARCH := x64 +else ifeq ($(ARCH),x86) + OVMF_ARCH := ia32 +else ifneq (,$(findstring arm,$(ARCH))) + OVMF_ARCH := arm +else + OVMF_ARCH := $(ARCH) +endif + +ifeq ($(ARCH),$(shell uname -m)) + KVMFLAGS := -enable-kvm -cpu host +else + KVMFLAGS := +endif + +QEMU_OVMF ?= /usr/share/ovmf/$(OVMF_ARCH)/OVMF.fd +QEMU_SMP ?= 2 +QEMU_MEM ?= 1G + +partition: blkcheck + $(DOSU) fdisk $(BLKDEV) <<<$$'g\nw\n' + $(DOSU) sfdisk $(BLKDEV) <<<$$',1G,C12A7328-F81F-11D2-BA4B-00A0C93EC93B,*\n,,0FC63DAF-8483-4772-8E79-3D69D8477DE4,\n' + +$(MOUNTPOINT)/boot/startup.nsh: + $(MAKE) blkcheck + @echo 'vmlinuz-$(KERNEL_FLAVOR) rw root=UUID=$(shell lsblk -rno UUID $(BLKDEV)$(P)$(ROOTFS_PART)) initrd=\initramfs-$(KERNEL_FLAVOR)' \ + | $(DOSU) tee $@ + +$(MOUNTPOINT)/.bootloader-done: $(MOUNTPOINT)/boot/startup.nsh + touch $@ + +qemu-test: umount + $(DOSU) $(WAYLAND_ASSIST) qemu-system-$(ARCH) \ + $(KVMFLAGS) \ + -smp $(QEMU_SMP) \ + -m $(QEMU_MEM) \ + -bios $(QEMU_OVMF) \ + -vga std -display gtk \ + -drive driver=raw,file.filename=$(BLKDEV) \ + -net user diff --git a/modules/bash-util b/modules/bash-util new file mode 160000 index 0000000..bd3ddab --- /dev/null +++ b/modules/bash-util @@ -0,0 +1 @@ +Subproject commit bd3ddab28f6a754e7ffc15e88257614aff0892ff