Files
AlvinArch/AGENTS.md
T
alvinlollo 1e8a1e3282
Upstream pin check / quickshell-pin (push) Successful in 46s
Tests / tests (push) Failing after 2m54s
fix(ci): Update Qt version to latest
2026-10-08 18:19:34 +11:00

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 .sh files checked with shellcheck -x -s bash
  • yamllint: all .yml/.yaml files (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_64 must be resolvable via pacman -Si in allowed repos (cachyos, extra, multilib, chaotic-aur), OR have a pre-built .pkg.tar.zst in localrepo/, OR appear in localrepo/build-end4-deps.sh

Package Management

Three sources contribute packages to the ISO:

  1. packages.x86_64 — main list, installed by mkarchiso. Must be in an allowed repo.
  2. localrepo/*.pkg.tar.zst — pre-built AUR packages. The local-aur repo is configured in pacman.conf and picked up by mkarchiso automatically.
  3. 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, 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: gum and UEFI — check_prereqs exits if /sys/firmware/efi is missing. Log: /var/log/alvin-install.log (copied into the target at the end; ERR trap prints the failing line).
  • Structure: main() runs 16 screen_* 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 of install_* 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. An EXIT trap unmounts $TARGET and closes cryptroot on any exit.
  • Package list: install_pacstrap builds the installed system's list from the medium's /run/archiso/bootmnt/arch/pkglist.x86_64.txt (i.e. pacman -Q of the live ISO), so packages.x86_64 changes flow into installed systems. It drops PKGS_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 …).
  • CI never lints it: the shellcheck step globs only *.sh. Check manually after edits: 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 with mkarchiso -v -w "$PWD/work" -o "$PWD/out" -- "$PWD" (.gitea/workflows/build-iso.yaml), and the local build command above does the same — never pass a releng path.
  • customize_airootfs.sh clones git repos (oh-my-zsh, lazy.nvim, plugins, end-4 dots) into /etc/skel and /opt during the ISO build. These require network access at build time but make the live ISO work offline.
  • customize_airootfs.sh runs ./setup install-deps -f -s --skip-plasmaintg as the throwaway user ii-builder (end-4's scripts refuse to run as root) and aborts the build if it fails. --skip-plasmaintg is deliberate: that optional Firefox widget pulls 410 packages / 526 MiB of KDE.
  • The [local-aur] repo path in pacman.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] at file:///opt/localrepo (see .live-pacman.conf). It holds localrepo/ (copied by CI/update.sh) plus everything install-deps built. pacstrap runs without -P, so the target keeps the stock /etc/pacman.conf; alvin-install strips [local-aur] from it anyway as a safety net, because /opt/localrepo exists only on the medium.
  • alvin-install runs end-4 with --skip-alldeps — dependencies must come from the ISO. It feeds yes i to end-4's failure prompt: inside the installer chroot the medium's /run is bind-mounted in, so end-4's systemctl --machine=…@.host --user enable … --now lines can never work (and --now would start services on the live medium). install_end4_dots re-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 that hyprctl/quickshell/the venv were resolvable (harvest) before re-running anything.
  • /etc/skel/.cache/uv + /etc/skel/.local/share/uv/python are pre-warmed by the build (~460 MB: CPython 3.12 and the pinned wheel cache) so end-4's uv venv/uv pip install at install time needs no network.
  • end4-pC (pctrade/end4-pC) is the default quickshell config: cloned into /etc/skel/.config/quickshell/end4-pC at build time, selected by patching hl.env("qsConfig", "ii") → "end4-pC" in the staged /opt/dots-hyprland/dots/.config/hypr/hyprland/variables.lua (there is no hypr/hyprland/ in this repo — ./setup install copies it to the installed system). alvin-install re-copies end4-pC into the user's home after ./setup install, because end-4's installer rsync --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 fix 5d5d498 (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 with Meta Types must be fully defined in the moc build of quickshell-hyprland-ipc (failed CI run 137). customize_airootfs.sh therefore rewrites end-4's _commit to 11ca60be after 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 (weekly pin-check.yaml job) 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.
  • packages.x86_64 is 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 gitignored airootfs/root/.iso-build-stamp when a .git dir exists or CI/GITEA_ACTIONS is set — line reads AlvinArch build <date> | commit <date> | <4-char hash> (falls back to commit unknown if git -c safe.directory can't read the repo; without .git/CI no file is staged). customize_airootfs.sh keeps it as /etc/alvin-build-stamp on 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-install has no motd step (an earlier install_motd that copied the banner + stamp into the target was removed on purpose).