11 KiB
AGENTS.md
Arch Linux ISO profile (archiso). Builds a live ISO with Hyprland desktop, end-4 dots-hyprland, and curated packages. Hosted on Gitea.
Build & Test Commands
# Build the ISO (repo root IS the profile directory)
sudo mkarchiso -v -w work -o out/ .
# Build AUR deps for end-4 (run locally before ISO build if localrepo/ is stale)
./localrepo/build-end4-deps.sh # build all
./localrepo/build-end4-deps.sh --quick # skip large AUR pkgs
# Refresh localrepo DB after adding/moving .pkg.tar.zst files
./localrepo/update.sh # re-indexes existing packages
./localrepo/update.sh pkg1.pkg.tar.zst # add files, then re-index
update.sh also refreshes airootfs/opt/localrepo/ (the copy that ships on the
ISO), so run it after touching localrepo/ before building.
CI Checks (must pass before merge)
- ShellCheck: all
.shfiles, plus extensionless bash scripts inairootfs/usr/local/bin/(by shebang), checked withshellcheck -x -s bash - yamllint: all
.yml/.yamlfiles (relaxed config) - packages.x86_64 syntax: one package per line, no trailing whitespace,
[a-zA-Z0-9._@+\-]+only - Package repo check: every package in
packages.x86_64must be resolvable viapacman -Siin allowed repos (cachyos,extra,multilib,chaotic-aur), OR have a pre-built.pkg.tar.zstinlocalrepo/, OR appear inlocalrepo/build-end4-deps.sh
Package Management
Three sources contribute packages to the ISO:
packages.x86_64— main list, installed by mkarchiso. Must be in an allowed repo.localrepo/*.pkg.tar.zst— pre-built AUR packages. Thelocal-aurrepo is configured inpacman.confand picked up by mkarchiso automatically.build-end4-deps.sh— builds end-4 AUR deps at build time.
There is a fourth source: customize_airootfs.sh runs end-4's own
./setup install-deps inside the image (as a throwaway ii-builder account),
which resolves and compiles everything the dotfiles need (quickshell, MicroTeX,
theming, fonts, ...). Those packages are installed in the live system, land in
pkglist.x86_64.txt, and are harvested into /opt/localrepo so pacstrap can
install them into the target without the network.
Important: localrepo/update.sh auto-appends new package names to packages.x86_64. If you remove a package from packages.x86_64 that exists in localrepo/, it will be re-added on the next update.sh run.
Key Files
| File | Purpose |
|---|---|
packages.x86_64 |
Live ISO package list (~402 packages) |
profiledef.sh |
ISO metadata, compression, boot modes, build-stamp staging |
pacman.conf |
Repos: cachyos → core → extra → multilib → chaotic-aur → local-aur |
bootstrap_packages |
Minimal build-time packages (base, arch-install-scripts) |
airootfs/root/customize_airootfs.sh |
Runs inside chroot during build: keyrings, skel dotfiles (incl. end4-pC), qsConfig patch, end-4 install-deps, package harvest, uv/font pre-warm |
airootfs/root/.live-pacman.conf |
/etc/pacman.conf of the built image (adds [local-aur] = file:///opt/localrepo) |
airootfs/usr/local/bin/alvin-install |
Installer: partitions, pacstrap, LUKS (systemd initramfs or experimental unl0kr + optional TPM2 unlock), user (fish shell), git identity + GitHub SSH keys, login manager (none/greetd/sddm/gdm) + autologin, optional sshd (default on), end-4 dotfiles (--skip-alldeps), boot |
localrepo/build-end4-deps.sh |
Non-interactive AUR builds for end-4 dependencies |
localrepo/update.sh |
Regenerates localrepo DB, syncs names to packages.x86_64, syncs airootfs/opt/localrepo/ |
localrepo/check-quickshell-pin.sh |
Tripwire: our quickshell pin and end-4's must include the Qt 6.12 fix and stay <45d old (QUICKSHELL_PIN_MAX_AGE_DAYS overrides) |
.gitea/workflows/pin-check.yaml |
Runs the pin check weekly (Mon 06:00), on dispatch, and on pushes touching pins/customize_airootfs.sh |
alvin-install (installer inside the ISO)
Source: airootfs/usr/local/bin/alvin-install (~1,880 lines, no .sh extension).
The airootfs overlay ships it as /usr/local/bin/alvin-install (mode 0755 forced in
profiledef.sh file_permissions), and /etc/motd advertises it on the live medium.
- Invoke: as root from the live session. Hard requirements:
gumand UEFI —check_prereqsexits if/sys/firmware/efiis missing. Log:/var/log/alvin-install.log(copied into the target at the end;ERRtrap prints the failing line). - Structure:
main()runs 16screen_*TUI functions (collect answers: welcome, keymap, network, disk, luks, unl0kr, filesystem, username, password, hostname, git, timezone, locale, login manager, sshd, summary) then a fixed order ofinstall_*steps (partition → LUKS → format/mount → pacstrap → fstab → repos → timezone → locale → hostname → network → sshd → user → git/SSH → end-4 dots → mkinitcpio → bootloader → TPM/unl0kr → login manager → autologin → cleanup). Screens and install steps are independent — edit one without touching the other. AnEXITtrap unmounts$TARGETand closescryptrooton any exit. - Package list:
install_pacstrapbuilds the installed system's list from the medium's/run/archiso/bootmnt/arch/pkglist.x86_64.txt(i.e.pacman -Qof the live ISO), sopackages.x86_64changes flow into installed systems. It dropsPKGS_EXCLUDE(archiso mkinitcpio-archiso mkinitcpio-nfs-utils syslinux memtest86+) plus names not resolvable in the live repos, then always appends hardcoded essentials (base linux linux-firmware linux-cachyos networkmanager grub efibootmgr sudo git …). - Linting: CI's shellcheck step picks it up by its bash shebang. Locally:
shellcheck -x -s bash airootfs/usr/local/bin/alvin-install - end-4 quirks (
--skip-alldeps,yes i, end4-pC re-copy) are in Gotchas below.
Verify a single change locally (mirrors CI)
shellcheck -x -s bash <script>.sh # plus the alvin-install command above
yamllint -d "{extends: relaxed, rules: {document-start: disable, line-length: disable, new-line-at-end-of-file: disable}}" <file>.yaml
packages.x86_64 syntax: one package per line, [a-zA-Z0-9._@+\-]+ only, no trailing
whitespace.
Gotchas
- The profile root is the repo root (not a
releng/subdirectory). CI builds withmkarchiso -v -w "$PWD/work" -o "$PWD/out" -- "$PWD"(.gitea/workflows/build-iso.yaml), and the local build command above does the same — never pass arelengpath. customize_airootfs.shclones git repos (oh-my-zsh, lazy.nvim, plugins, end-4 dots) into/etc/skeland/optduring the ISO build. These require network access at build time but make the live ISO work offline.customize_airootfs.shruns./setup install-deps -f -s --skip-plasmaintgas the throwaway userii-builder(end-4's scripts refuse to run as root) and aborts the build if it fails.--skip-plasmaintgis deliberate: that optional Firefox widget pulls 410 packages / 526 MiB of KDE.- The
[local-aur]repo path inpacman.conf(file:///home/alvin/AlvinArch/localrepo) is for local builds only. The CI does not use this path. - The image ships its own
[local-aur]atfile:///opt/localrepo(see.live-pacman.conf). It holdslocalrepo/(copied by CI/update.sh) plus everythinginstall-depsbuilt. pacstrap runs without-P, so the target keeps the stock/etc/pacman.conf;alvin-installstrips[local-aur]from it anyway as a safety net, because/opt/localrepoexists only on the medium. alvin-installruns end-4 with--skip-alldeps— dependencies must come from the ISO. It feedsyes ito end-4's failure prompt: inside the installer chroot the medium's/runis bind-mounted in, so end-4'ssystemctl --machine=…@.host --user enable … --nowlines can never work (and--nowwould start services on the live medium).install_end4_dotsre-applies the enable-only part (bluetooth, ydotool, i2c module) outside the chroot and reports every step end-4 had to skip. If the end-4 step reports "incomplete", check thathyprctl/quickshell/the venv were resolvable (harvest) before re-running anything./etc/skel/.cache/uv+/etc/skel/.local/share/uv/pythonare pre-warmed by the build (~460 MB: CPython 3.12 and the pinned wheel cache) so end-4'suv venv/uv pip installat install time needs no network.- end4-pC (
pctrade/end4-pC) is the default quickshell config: cloned into/etc/skel/.config/quickshell/end4-pCat build time, selected by patchinghl.env("qsConfig", "ii")→"end4-pC"in the staged/opt/dots-hyprland/dots/.config/hypr/hyprland/variables.lua(there is nohypr/hyprland/in this repo —./setup installcopies it to the installed system).alvin-installre-copies end4-pC into the user's home after./setup install, because end-4's installerrsync --deletes all of~/.config/quickshell. airootfs/etc/skel/.config/illogical-impulse/presets/carries the user's presets (Laptop.json,Personal-Performance.json,Personal-Performance.zip). They embed/home/alvin/...paths, so wallpapers/screenshots need fixing up after install.- End-4 metapackages are built from
localrepo/end4-pkgbuilds/(quickshell-git, bibata-modern-classic-bin, microtex-git). - quickshell pin / Qt 6.12: end-4 pins quickshell to
41651d7(Sep 25), which predates upstream fix5d5d498(Oct 3, "use MOC includes instead of opaque pointer definitions — … Qt 6.12 compatibility"). Arch shipped qt6 6.12.0 on Oct 8 and the old pin died withMeta Types must be fully definedin the moc build of quickshell-hyprland-ipc (failed CI run 137).customize_airootfs.shtherefore rewrites end-4's_committo11ca60beafter cloning — but only while it still matches^_commit='41651d7, so once end-4 bumps their own pin the guard goes inert and theirs wins.localrepo/check-quickshell-pin.sh(weeklypin-check.yamljob) fails CI if either pin goes stale or loses the fix; it warns instead of failing when end-4's known-stale pin is covered by the override. /etc/skel/.zshrcis owned bygrml-zsh-config, so ours is staged asairootfs/root/.zshrc-skeland installed bycustomize_airootfs.shafter pacstrap — never add files underairootfs/at paths a package owns, or pacstrap aborts with "exists in filesystem".packages.x86_64is sorted alphabetically. Keep it sorted when adding/removing entries.airootfs/opt/localrepo/is a build artifact (gitignored) — never commit it.- Build stamp flow:
profiledef.sh(sourced by mkarchiso) stages the gitignoredairootfs/root/.iso-build-stampwhen a.gitdir exists orCI/GITEA_ACTIONSis set — line readsAlvinArch build <date> | commit <date> | <4-char hash>(falls back tocommit unknownifgit -c safe.directorycan't read the repo; without.git/CI no file is staged).customize_airootfs.shkeeps it as/etc/alvin-build-stampon the live medium and appends it to the live/etc/motd, so the booted ISO identifies its build. The installed system's motd is never touched by us —alvin-installhas no motd step (an earlierinstall_motdthat copied the banner + stamp into the target was removed on purpose).