Author SHA1 Message Date
alvinlollo e57143c4bc docs/ci: implement yay 13 SIGSEGV workaround and update docs
Upstream pin check / quickshell-pin (push) Successful in 28s
Tests / tests (push) Successful in 3m31s
2026-10-12 12:57:37 +11:00
alvinlollo 3275ec9845 chore: add build runner CAP_SETFCAP check, fix MicroTeX compilation, and fix mono cert-sync
Upstream pin check / quickshell-pin (push) Successful in 18s
Tests / tests (push) Successful in 2m55s
2026-10-11 19:41:08 +11:00
alvinlolloandClaude Opus 5.5 b68623a782 docs: Document the zsh-autocomplete partial-load error and fixes
Upstream pin check / quickshell-pin (push) Successful in 20s
Tests / tests (push) Successful in 3m0s
Adds a Troubleshooting entry (symptom, cause, drop-or-keep fixes). The
keep path removes grml-zsh-config instead of setting
skip_global_compinit=1: grml's /etc/zsh/zshrc runs compinit
unconditionally and ignores that Debian/Ubuntu variable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:08:30 +11:00
alvinlolloandClaude Opus 5.5 88e73bbc22 docs: Sync AGENTS.md with CI lint, fish default, zshrc staging
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:41:39 +11:00
alvinlolloandClaude Opus 5.5 025082a92f fix(localrepo): Exact local package match, no .old on medium, rebuild on bump
- tests: match localrepo packages by exact name (foo no longer satisfied
  by foo-bar-*.pkg.tar.zst).
- update.sh: copy the db/files by name so repo-add's *.old backups stay
  off the medium.
- build-end4-deps.sh: rebuild a local PKGBUILD when it is newer than the
  built package (a quickshell _commit bump used to be skipped).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:41:38 +11:00
alvinlolloandClaude Opus 5.5 699ed6a935 fix(installer): Validate hostname, parse disk models, skip zram
- screen_hostname: reject names that break /etc/hostname and /etc/hosts.
- screen_disk: parse lsblk -P by regex so multi-word/empty MODELs show
  correctly, and never offer /dev/zram* (TYPE=disk) as a target.
- packages: add python-distro, the undeclared runtime dep of the shipped
  ssh-import-id (it always crashed and fell back to curl).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:41:38 +11:00
alvinlolloandClaude Opus 5.5 e63f9cb14f fix(iso): Ship an archiso preset for linux-cachyos, the kernel we boot
linux.preset targeted the 'linux' package, which is not on the ISO, so
linux-cachyos generated its own default+fallback preset and the squashfs
carried an unused fallback initramfs. No package owns the preset file
(mkinitcpio's hook only creates one if missing), so this cannot conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:41:38 +11:00
alvinlolloandClaude Opus 5.5 c5c7ab9b8b fix(ci): Lint extensionless scripts, serialize tests, drop pinned URLs
- ShellCheck now also covers bash scripts without .sh (alvin-install,
  choose-mirror, livecd-sound), picked up by shebang.
- concurrency group: the job rewrites the runner's /etc/pacman.conf.
- Import the CachyOS key from the keyserver instead of pacman -U on
  pinned keyring/mirrorlist URLs that break once the mirror prunes them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:39:22 +11:00
alvinlolloandClaude Opus 5.5 03f0ca05e0 fix(build): Verify cachyos and chaotic-aur signatures at ISO build time
Both repos used SigLevel = Optional TrustAll, so unsigned or tampered
packages went straight into the ISO. Inherit Required DatabaseOptional
from [options] and have build-iso import + lsign both signing keys.

Also in build-iso: detach only loop devices backed by the work dir
(losetup -D hit every loop on the runner host) and drop the ?token=
download fallback that put the API token into access logs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:39:22 +11:00
alvinlolloandClaude Opus 5.5 5299b90bbc fix(skel): Keep script exec bits and ship a .zshrc for oh-my-zsh
- Replace the 644/755 chmod sweep (which also undid its own chmod 700)
  with go-w,a+rX so non-.sh executables keep +x.
- Install an oh-my-zsh .zshrc into /etc/skel after pacstrap. It is staged
  as /root/.zshrc-skel because grml-zsh-config owns /etc/skel/.zshrc and
  an airootfs copy would abort pacstrap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:38:11 +11:00
alvinlolloandClaude Opus 5.5 55b66a1373 fix(installer): Survive copytoram, keep ISO repos, default to fish
- install_pacstrap: fall back to the live system's pacman -Q when the
  medium's pkglist is gone (copytoram unmounts bootmnt), and die on an
  empty list instead of pacstrapping only the essentials.
- screen_disk: find the live medium via archisosearchuuid when bootmnt
  is unmounted, and hide whole-disk sources (empty PKNAME).
- install_cachyos_repo: put [cachyos] ahead of [core] like the ISO build
  and add [chaotic-aur], so chaotic packages keep updating.
- install_user: create the user with fish; the 'none' login manager now
  autostarts Hyprland via ~/.config/fish/conf.d/alvin-hyprland.fish.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:38:11 +11:00
alvinlolloandClaude Opus 5.5 504a568570 chore(localrepo): Stop tracking makepkg output and repo-add backups
build-end4-deps.sh builds in place, so pkg/, src/ and the VCS mirrors
under end4-pkgbuilds/ (incl. a built quickshell tree) ended up in git.
Ignore everything there except PKGBUILDs and hooks, plus *.old backups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:38:00 +11:00
alvinlolloandClaude Opus 5.5 0ae2f036b7 fix(ci): Stop bash -e from killing the conflict check silently
Tests / tests (push) Successful in 3m29s
The `pacman -S` in the conflict check exits non-zero by design (a
conflict fails in prepare, a clean list fails at the disabled download),
and `OUT=$(...)` carries that status, so `bash -e` aborted the step
before the output was inspected: exit 1 with no message on every run.
Ignore the status and judge by the output, as intended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:25:17 +11:00
alvinlolloandClaude Opus 5.5 0e57df4360 fix(ci): Make the package conflict check actually detect conflicts
Tests / tests (push) Failing after 2m53s
The step ran `pacman -Sp`, which skips conflict checking entirely: it
happily prints URLs for eternalterminal + eternalterminal-client and
exits 0. It also skipped every localrepo package, so the conflict fixed
in f06bfd1 could never have been caught here.

Now the check resolves against a throwaway config that adds [local-aur]
(localrepo/), with a fresh dbpath/root so runner-installed packages
don't mask or fake conflicts, and runs a real `pacman -S`. Conflicts and
broken deps fail in "prepare", before any download; XferCommand is set
to /usr/bin/false after the sync so a clean list stops at the download
instead of fetching the whole ISO. Package availability comes from one
`-Slq` instead of ~400 `-Si` calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:50:35 +11:00
alvinlolloandClaude Opus 5.5 da26e88147 fix(localrepo): Drop stale packages so update.sh can't re-add them
f06bfd1 and a940d91 removed eternalterminal-client/-server,
illogical-impulse-quickshell-git, meshy-git and pacserve from
packages.x86_64, but their .pkg.tar.zst files stayed in localrepo/ and
in local-aur.db. update.sh appends every localrepo package name back to
the list, which would bring the eternalterminal conflict back, and CI
shipped the conflicting packages into the image's /opt/localrepo.

Remove the files and their repo DB entries, re-sort the vulkan-* lines
in packages.x86_64, and drop the names from README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:39:06 +11:00
alvinlollo f06bfd1512 fix(ci): Remove conflicting packages
Tests / tests (push) Successful in 4m36s
2026-10-08 21:44:54 +11:00
alvinlollo a940d91794 fix(ci): Fix end4 dependecies
Tests / tests (push) Successful in 4m35s
2026-10-08 20:53:54 +11:00
alvinlollo 9af4bc6c27 fix(tests): Fix yamlint compliance with whitespace
Tests / tests (push) Successful in 4m31s
2026-10-08 19:01:25 +11:00
alvinlollo 3c540f8bed fix(test): Fix yaml formatting
Tests / tests (push) Failing after 24s
2026-10-08 18:55:27 +11:00
alvinlollo 3f3bba05f3 fix(test): Fix formatting
Tests / tests (push) Failing after 25s
2026-10-08 18:53:51 +11:00
alvinlollo 01913f7682 feat(jobs): Output conflicting packages
Tests / tests (push) Failing after 2m24s
2026-10-08 18:46:47 +11:00
alvinlollo f9a2978517 Update MicroTeX submodule pointer
Tests / tests (push) Failing after 2m30s
2026-10-08 18:35:41 +11:00
alvinlollo 1e8a1e3282 fix(ci): Update Qt version to latest
Upstream pin check / quickshell-pin (push) Successful in 46s
Tests / tests (push) Failing after 2m54s
2026-10-08 18:19:34 +11:00
alvinlollo db83549996 feat(gum): Add further additions of gum to install script
Tests / tests (push) Successful in 2m29s
2026-10-08 14:48:20 +11:00
alvinlollo 59d82f06f5 feat(installer): sshd opt-in, gsettings replay, live-medium motd stamp
Tests / tests (push) Successful in 2m50s
- alvin-install: new sshd screen (default on) + install step. openssh is
  guaranteed into the pacstrap list when requested, and the final banner
  reports whether the enable actually succeeded (SSHD_APPLIED) instead of
  what was selected. The prompt warns that password login uses the same
  password as LUKS, and only mentions the GitHub key import when a
  username was given.
- alvin-install: replay the `gsettings set` commands end-4 logged during
  its setup run with a clean env and a throwaway session bus as the target
  user, then read each key back in a second session to confirm it persisted
  (gvariant_unescape normalizes both '...' and "..." GVariant output). The
  venv check now walks the uv bin/python symlink chain, re-rooting
  absolute hops under $TARGET, since that link dangles on the live medium.
- build: append the build stamp to the live /etc/motd (keeps the installed
  system's motd untouched) and drop alvin-install's install_motd step.
- docs: installer sections in README/AGENTS.md, sshd notes, corrected line
  counts.
2026-10-07 21:55:30 +11:00
27 changed files with 1377 additions and 240 deletions
+54 -6
View File
@@ -101,6 +101,36 @@ jobs:
exit 1 exit 1
fi fi
- name: Trust cachyos and chaotic-aur signing keys
run: |
set -euo pipefail
# The profile's pacman.conf verifies [cachyos] and [chaotic-aur]
# signatures (SigLevel inherited from [options]); mkarchiso's
# pacstrap checks them against the host keyring.
# F3B607488DB35A47 CachyOS <admin@cachyos.org>
# 3056513887B78AEB chaotic-aur (Pedro Henrique Lara Campos)
for key in F3B607488DB35A47 3056513887B78AEB; do
if pacman-key --list-keys "$key" >/dev/null 2>&1; then
echo " ✅ $key already in the keyring"
else
got=0
# Retry: gateway drops UDP/53 in bursts (see run 131)
for try in 1 2 3 4 5; do
if pacman-key --recv-keys "$key" --keyserver keyserver.ubuntu.com; then
got=1
break
fi
echo " ⚠️ recv-keys $key failed (attempt $try/5), retrying in $(( try * 15 ))s..." >&2
sleep $(( try * 15 ))
done
if [ "$got" -ne 1 ]; then
echo "❌ Could not fetch signing key $key" >&2
exit 1
fi
fi
pacman-key --lsign-key "$key"
done
- name: Ensure /dev/fd symlinks exist for mkinitcpio and procsub - name: Ensure /dev/fd symlinks exist for mkinitcpio and procsub
run: | run: |
set -euo pipefail set -euo pipefail
@@ -132,6 +162,23 @@ jobs:
rmdir "$d" rmdir "$d"
trap - EXIT trap - EXIT
- name: Require CAP_SETFCAP for file capabilities
run: |
set -euo pipefail
# Without CAP_SETFCAP, pacstrap cannot restore security.capability
# xattrs ("unable to set CAP_SETFCAP effective capability"), so the
# live ISO silently ships newuidmap/newgidmap, snap-confine, dumpcap,
# kwin_wayland, ... without their capabilities. Fix the runner
# (e.g. allow CAP_SETFCAP in the gitea-runner service) rather than
# building a broken image.
eff="$(awk '/^CapEff:/{print $2}' /proc/self/status)"
if (( (16#$eff >> 31) & 1 )); then
echo "✅ CAP_SETFCAP is available (CapEff=$eff)"
else
echo "❌ CAP_SETFCAP missing (CapEff=$eff) - file capabilities would be dropped from the ISO" >&2
exit 1
fi
- name: Build Arch ISO - name: Build Arch ISO
run: | run: |
set -euo pipefail set -euo pipefail
@@ -503,13 +550,11 @@ jobs:
url="$GITEA_ORIGIN/$REPOSITORY/releases/download/$TAG_NAME/$enc_fname" url="$GITEA_ORIGIN/$REPOSITORY/releases/download/$TAG_NAME/$enc_fname"
downloaded=0 downloaded=0
for attempt in 1 2 3; do for attempt in 1 2 3; do
# Try header auth, then ?token=, then anonymous (public repo). # Try header auth, then anonymous (public repo). No ?token=
# fallback: a token in the query string lands in access logs.
if curl -sfL --max-time 600 -H "Authorization: token $GITEA_TOKEN" \ if curl -sfL --max-time 600 -H "Authorization: token $GITEA_TOKEN" \
"$url" -o "$tmpdir/$fname"; then "$url" -o "$tmpdir/$fname"; then
downloaded=1 downloaded=1
elif curl -sfL --max-time 600 "$url?token=$GITEA_TOKEN" \
-o "$tmpdir/$fname"; then
downloaded=1
elif curl -sfL --max-time 600 "$url" -o "$tmpdir/$fname"; then elif curl -sfL --max-time 600 "$url" -o "$tmpdir/$fname"; then
downloaded=1 downloaded=1
fi fi
@@ -572,8 +617,11 @@ jobs:
# Unmount any leftover loop devices or bind mounts in the work directory # Unmount any leftover loop devices or bind mounts in the work directory
$PRIV umount -R "$PWD/work" 2>/dev/null || true $PRIV umount -R "$PWD/work" 2>/dev/null || true
# Clean up loop devices if any remain attached to the work dir # Detach only loop devices backed by files in our work dir:
$PRIV losetup -D || true # `losetup -D` would detach every loop device on the runner host.
$PRIV losetup -nO NAME,BACK-FILE 2>/dev/null \
| awk -v w="$PWD/work/" 'index($2, w) == 1 {print $1}' \
| while read -r dev; do $PRIV losetup -d "$dev" || true; done
# Remove working directories with proper permissions # Remove working directories with proper permissions
$PRIV rm -rf "$PWD/work" $PRIV rm -rf "$PWD/work"
+26
View File
@@ -0,0 +1,26 @@
name: Upstream pin check
on:
schedule:
- cron: "0 6 * * 1" # Mondays 06:00 UTC — catches upstream/Qt drift between ISO builds
workflow_dispatch:
push:
paths:
- "localrepo/end4-pkgbuilds/quickshell-git/PKGBUILD"
- "localrepo/check-quickshell-pin.sh"
- "airootfs/root/customize_airootfs.sh"
- ".gitea/workflows/pin-check.yaml"
jobs:
quickshell-pin:
runs-on: archlinux
steps:
- name: Checkout repo
uses: actions/checkout@v4
# Tripwire for the failure mode that broke ISO build run 137:
# a quickshell pin (ours or end-4's) that predates a Qt compat fix
# or has simply gone stale. Fails the job so the drift is visible
# in Actions instead of surfacing as a 3-hour ISO build failure.
- name: Check quickshell pins
run: ./localrepo/check-quickshell-pin.sh
+84 -24
View File
@@ -5,6 +5,12 @@ on:
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
# This job rewrites the runner's shared /etc/pacman.conf - never let two
# runs do that at the same time.
concurrency:
group: host-pacman-conf
cancel-in-progress: false
jobs: jobs:
tests: tests:
runs-on: archlinux runs-on: archlinux
@@ -56,14 +62,12 @@ jobs:
- name: Set up CachyOS repo - name: Set up CachyOS repo
run: | run: |
# Key straight from the keyserver; no pinned keyring/mirrorlist
# package URLs (they 404 once the mirror prunes that version, and
# the mirrorlist was overwritten below anyway).
pacman-key --recv-keys F3B607488DB35A47 --keyserver keyserver.ubuntu.com pacman-key --recv-keys F3B607488DB35A47 --keyserver keyserver.ubuntu.com
pacman-key --lsign-key F3B607488DB35A47 pacman-key --lsign-key F3B607488DB35A47
pacman -U --noconfirm \
'https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-keyring-20240331-1-any.pkg.tar.zst' \
'https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-mirrorlist-27-1-any.pkg.tar.zst'
# Fix the mirrorlist URL (pacnew has wrong format)
printf '%s\n' 'Server = https://mirror.cachyos.org/repo/$arch/$repo' \ printf '%s\n' 'Server = https://mirror.cachyos.org/repo/$arch/$repo' \
> /etc/pacman.d/cachyos-mirrorlist > /etc/pacman.d/cachyos-mirrorlist
@@ -112,10 +116,16 @@ jobs:
- name: Lint shell scripts with ShellCheck - name: Lint shell scripts with ShellCheck
run: | run: |
echo "═══════════════════════════════════════════════════════════════" echo "═══════════════════════════════════════════════════════════════"
echo " Running ShellCheck on all .sh files" echo " Running ShellCheck on all bash scripts"
echo "═══════════════════════════════════════════════════════════════" echo "═══════════════════════════════════════════════════════════════"
SH_FILES=$(find . -name '*.sh' -not -path './.git/*') # *.sh plus extensionless bash scripts (alvin-install, choose-mirror,
# livecd-sound, ...) picked up by their shebang
SH_FILES=$(
find . -name '*.sh' -not -path './.git/*'
find airootfs/usr/local/bin -type f ! -name '*.sh' \
-exec grep -lE '^#!(/usr/bin/env bash|/bin/bash)' {} +
)
if [ -z "$SH_FILES" ]; then if [ -z "$SH_FILES" ]; then
echo "No shell scripts found." echo "No shell scripts found."
exit 0 exit 0
@@ -258,10 +268,18 @@ jobs:
done done
# 2) Check localrepo/ for a pre-built .pkg.tar.zst # 2) Check localrepo/ for a pre-built .pkg.tar.zst
if [[ $MATCHED -eq 0 ]] && compgen -G "localrepo/${pkg}-*.pkg.tar.zst" > /dev/null 2>&1; then # Exact name: strip -<pkgver>-<pkgrel>-<arch>.pkg.tar.zst, so
MATCHED=1 # "foo" is not satisfied by foo-bar-1.0-1-any.pkg.tar.zst
FOUND=$((FOUND + 1)) if [[ $MATCHED -eq 0 ]]; then
echo "📦 LOCAL: $pkg (pre-built in localrepo/)" for f in localrepo/"${pkg}"-*.pkg.tar.zst; do
b=${f##*/}
if [[ "${b%-*-*-*}" == "$pkg" ]]; then
MATCHED=1
FOUND=$((FOUND + 1))
echo "📦 LOCAL: $pkg (pre-built in localrepo/)"
break
fi
done
fi fi
# 3) Check build-end4-deps.sh for the package name # 3) Check build-end4-deps.sh for the package name
@@ -309,23 +327,65 @@ jobs:
echo " Checking for conflicting packages in packages.x86_64" echo " Checking for conflicting packages in packages.x86_64"
echo "═══════════════════════════════════════════════════════════════" echo "═══════════════════════════════════════════════════════════════"
# Collect all package names from the list # Resolve against the repos mkarchiso sees, including [local-aur]
# (localrepo/), so conflicts between pre-built AUR packages and
# anything else are caught here instead of in the ISO build.
# A fresh dbpath = empty local db, like pacstrap's target root:
# packages installed on the runner can neither mask a conflict
# nor cause a false one.
CONF=$(mktemp)
DB=$(mktemp -d)
ROOT=$(mktemp -d)
cp /etc/pacman.conf "$CONF"
printf '\n[local-aur]\nSigLevel = Optional TrustAll\nServer = file://%s/localrepo\n' \
"$PWD" >> "$CONF"
mkdir -p "$DB/sync"
cp -p /var/lib/pacman/sync/*.db "$DB/sync/"
pac() { pacman --config "$CONF" --dbpath "$DB" "$@"; }
pac -Sy --noconfirm
# From here on never download (set after -Sy, which needs to fetch
# the file:// local-aur db): the check only needs a prepared
# transaction
sed -i '/^\[options\]/a XferCommand = /usr/bin/false %u' "$CONF"
# Packages built at ISO build time (build-end4-deps.sh) can't be
# resolved yet; everything else must be
mapfile -t AVAIL < <(pac -Slq | sort -u)
PKGS=() PKGS=()
while IFS= read -r pkg; do while IFS= read -r pkg; do
[[ -z "$pkg" || "$pkg" =~ ^# ]] && continue [[ -z "$pkg" || "$pkg" =~ ^# ]] && continue
PKGS+=("$pkg") if printf '%s\n' "${AVAIL[@]}" | grep -qxF -- "$pkg"; then
PKGS+=("$pkg")
else
echo "⚠️ Skipping unresolvable package: $pkg"
fi
done < packages.x86_64 done < packages.x86_64
# Use pacman -Sp --print to resolve the full install set and check for conflicts # `pacman -Sp` skips conflict checks entirely, so run a real -S
# This catches "X and Y are in conflict" errors before the ISO build # into an empty root. Conflicts and broken deps fail in "prepare",
PKG_LIST=$(printf '%s\n' "${PKGS[@]}") # before any download; a clean list gets as far as the download,
if CONFLICTS=$(echo "$PKG_LIST" | pacman -Sp --needed - 2>&1); then # which the XferCommand above makes fail on purpose.
echo "✅ No package conflicts detected" if [ ${#PKGS[@]} -eq 0 ]; then
echo "✅ No resolvable packages to check."
else else
echo "$CONFLICTS" | grep -i "conflict" || true # pacman exits non-zero either way; judge by its output, not
echo "" # its status (bash -e would otherwise kill the step silently)
echo "═══════════════════════════════════════════════════════════════" OUT=$(pac -S --noconfirm --root "$ROOT" --cachedir "$ROOT" "${PKGS[@]}" 2>&1) || true
echo " ❌ Conflicting packages found — fix packages.x86_64" if grep -q 'failed to prepare transaction' <<< "$OUT"; then
echo "═══════════════════════════════════════════════════════════════" echo "------- Pacman Output -------"
exit 1 grep -vE '^(http|file|ftp)' <<< "$OUT" | tail -40
echo "-----------------------------"
echo ""
echo "═══════════════════════════════════════════════════════════════"
echo " ❌ Conflicting/unresolvable packages — fix packages.x86_64"
echo "═══════════════════════════════════════════════════════════════"
exit 1
elif ! grep -q 'Proceed with installation' <<< "$OUT"; then
echo "------- Pacman Output -------"
tail -40 <<< "$OUT"
echo "-----------------------------"
echo "❌ pacman stopped before the conflict check completed"
exit 1
fi
echo "✅ No package conflicts detected (repos + local-aur)"
fi fi
+8
View File
@@ -9,3 +9,11 @@ airootfs/opt/localrepo/
# Build stamp staged by profiledef.sh at ISO build time (see AGENTS.md); # Build stamp staged by profiledef.sh at ISO build time (see AGENTS.md);
# consumed by customize_airootfs.sh, never committed # consumed by customize_airootfs.sh, never committed
airootfs/root/.iso-build-stamp airootfs/root/.iso-build-stamp
# makepkg output from localrepo/build-end4-deps.sh (it builds in place):
# keep only the PKGBUILDs and hooks under end4-pkgbuilds/
localrepo/end4-pkgbuilds/*/*
!localrepo/end4-pkgbuilds/*/PKGBUILD
!localrepo/end4-pkgbuilds/*/*.hook
# repo-add backups
localrepo/*.old
+50 -5
View File
@@ -22,7 +22,7 @@ ISO), so run it after touching `localrepo/` before building.
## CI Checks (must pass before merge) ## CI Checks (must pass before merge)
- **ShellCheck**: all `.sh` files checked with `shellcheck -x -s bash` - **ShellCheck**: all `.sh` files, plus extensionless bash scripts in `airootfs/usr/local/bin/` (by shebang), checked with `shellcheck -x -s bash`
- **yamllint**: all `.yml`/`.yaml` files (relaxed config) - **yamllint**: all `.yml`/`.yaml` files (relaxed config)
- **packages.x86_64 syntax**: one package per line, no trailing whitespace, `[a-zA-Z0-9._@+\-]+` only - **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 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`
@@ -54,22 +54,67 @@ install them into the target without the network.
| `bootstrap_packages` | Minimal build-time packages (`base`, `arch-install-scripts`) | | `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/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/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, end-4 dotfiles (`--skip-alldeps`), build-stamp motd, boot | | `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/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/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 …`).
- **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](#gotchas) below.
## Verify a single change locally (mirrors CI)
```bash
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 ## Gotchas
- The profile root is the **repo root** (not a `releng/` subdirectory). The CI build-iso workflow uses `mkarchiso -v -w work -o out/ releng` but this is a naming convention — the actual profile files live at the repo root. - 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` 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. - `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 `[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. - 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. - `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. - `/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 --delete`s all of `~/.config/quickshell`. - **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 --delete`s 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. - `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). - 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.
- **MicroTeX flags**: the image's `makepkg.conf` is CachyOS's (`-march=native -O3`, LTO). GCC 16.2 hit an internal compiler error (segfault) on MicroTeX's `gtkmm_main.cpp` with it (failed CI run 155; not reproducible locally), so `customize_airootfs.sh` patches end-4's `illogical-impulse-microtex-git` PKGBUILD to `options=(!lto)` + `-O2` (our `localrepo/end4-pkgbuilds/microtex-git` carries the same).
- **CAP_SETFCAP on the runner**: without it pacstrap drops `security.capability` xattrs (newuidmap/newgidmap, snap-confine, dumpcap, kwin_wayland, ...) from the live ISO with only a warning. `build-iso.yaml` fails the job early if the runner lacks it — fix the runner, don't remove the check.
- **yay SIGSEGV on exit**: yay 13 (libalpm via purego/`dyalpm`) can segfault in `alpm_release()` *after* a finished transaction (failed CI run at end-4's `illogical-impulse-kde` step), which aborted `install-deps`. `customize_airootfs.sh` puts a temporary `yay` shim first on the builder's `PATH`: a failed plain `yay -S <pkgs>` counts as success only if `pacman -T` confirms every target is installed (it logs `[yay-shim]`). Every other failure keeps yay's exit code.
- mono's `cert-sync` runs before `ca-certificates.crt` exists during pacstrap, so `customize_airootfs.sh` (live) and `install_pacstrap` (target) re-run it.
- `/etc/skel/.zshrc` is owned by `grml-zsh-config`, so ours is staged as `airootfs/root/.zshrc-skel` and installed by `customize_airootfs.sh` after pacstrap — never add files under `airootfs/` at paths a package owns, or pacstrap aborts with "exists in filesystem".
- `packages.x86_64` is sorted alphabetically. Keep it sorted when adding/removing entries. - `packages.x86_64` is sorted alphabetically. Keep it sorted when adding/removing entries.
- `airootfs/opt/localrepo/` is a build artifact (gitignored) — never commit it. - `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` installs it as `/etc/alvin-build-stamp` on the live medium, and `alvin-install`'s `install_motd` copies the live `/etc/motd` banner to the target and appends the stamp. If the medium has no stamp, the installed system gets no motd from us. - **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).
Symlink
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+119 -9
View File
@@ -21,7 +21,6 @@ AlvinArch/
├── localrepo/ ├── localrepo/
│ ├── build-end4-deps.sh # non-interactive AUR build (all deps) │ ├── build-end4-deps.sh # non-interactive AUR build (all deps)
│ ├── end4-pkgbuilds/ # microtex-git, quickshell-git, bibata │ ├── end4-pkgbuilds/ # microtex-git, quickshell-git, bibata
│ ├── illogical-impulse-quickshell-git-*.pkg.tar.zst
│ ├── illogical-impulse-bibata-modern-classic-bin-*.pkg.tar.zst │ ├── illogical-impulse-bibata-modern-classic-bin-*.pkg.tar.zst
│ └── update.sh # existing, regenerates repo db │ └── update.sh # existing, regenerates repo db
└── packages.x86_64 # 420 packages (65+ end-4 deps added) └── packages.x86_64 # 420 packages (65+ end-4 deps added)
@@ -37,9 +36,10 @@ AlvinArch/
6. [System Services & Networking](#system-services--networking) 6. [System Services & Networking](#system-services--networking)
7. [Local Repository (localrepo/)](#local-repository-localrepo) 7. [Local Repository (localrepo/)](#local-repository-localrepo)
8. [Boot Configuration](#boot-configuration) 8. [Boot Configuration](#boot-configuration)
9. [Build & Test](#build--test) 9. [Automated Installer (alvin-install)](#automated-installer-alvin-install)
10. [Planned Additions](#planned-additions) 10. [Build & Test](#build--test)
11. [Troubleshooting](#troubleshooting) 11. [Planned Additions](#planned-additions)
12. [Troubleshooting](#troubleshooting)
--- ---
@@ -50,7 +50,7 @@ AlvinArch/
### Key Features ### Key Features
- **Hyprland + end-4** — Wayland compositor with illogical-impulse dotfiles (quickshell, bibata cursors, microtex) - **Hyprland + end-4** — Wayland compositor with illogical-impulse dotfiles (quickshell, bibata cursors, microtex)
- **Automated installer (`alvin-install`)** — gum TUI: LUKS with systemd initramfs or experimental touchscreen unl0kr, optional TPM2 auto-unlock, GitHub SSH key import, login manager choice (none/greetd/sddm/gdm), autologin, and a build stamp in `/etc/motd` - **Automated installer (`alvin-install`)** — gum TUI: LUKS with systemd initramfs or experimental touchscreen unl0kr, optional TPM2 auto-unlock, GitHub SSH key import, login manager choice (none/greetd/sddm/gdm), optional sshd (on by default), autologin; the build stamp identifies the live ISO via its `/etc/motd`
- **LazyVim** — Pre-configured Neovim with lazy.nvim and curated plugins - **LazyVim** — Pre-configured Neovim with lazy.nvim and curated plugins
- **Fish + Zsh + Starship** — Dual shell setup with oh-my-zsh and a cross-shell prompt - **Fish + Zsh + Starship** — Dual shell setup with oh-my-zsh and a cross-shell prompt
- **Local AUR repo** — Pre-built packages (end-4 deps, wallust, grub-customizer, etc.) that aren't in official repos - **Local AUR repo** — Pre-built packages (end-4 deps, wallust, grub-customizer, etc.) that aren't in official repos
@@ -138,7 +138,7 @@ AlvinArch/
│ │ └── mkinitcpio.conf.d/ # archiso initramfs config │ │ └── mkinitcpio.conf.d/ # archiso initramfs config
│ ├── usr/ │ ├── usr/
│ │ ├── lib/os-release # usr-merge os-release │ │ ├── lib/os-release # usr-merge os-release
│ │ ├── local/bin/ # choose-mirror, Installation_guide, livecd-sound │ │ ├── local/bin/ # alvin-install, choose-mirror, Installation_guide, livecd-sound
│ │ ├── local/share/livecd-sound/ # ALSA config (asound.conf.in) │ │ ├── local/share/livecd-sound/ # ALSA config (asound.conf.in)
│ │ └── share/pacman/keyrings/ # CachyOS GPG keyring + trust data │ │ └── share/pacman/keyrings/ # CachyOS GPG keyring + trust data
│ └── root/ │ └── root/
@@ -152,7 +152,7 @@ AlvinArch/
│ ├── update.sh # Regenerates repo DB + syncs names to packages.x86_64 │ ├── update.sh # Regenerates repo DB + syncs names to packages.x86_64
│ ├── end4-pkgbuilds/ # PKGBUILDs: quickshell-git, microtex-git, bibata-modern-classic-bin │ ├── end4-pkgbuilds/ # PKGBUILDs: quickshell-git, microtex-git, bibata-modern-classic-bin
│ ├── *.pkg.tar.zst # Pre-built packages (illogical-impulse-*, wallust, grub-customizer, │ ├── *.pkg.tar.zst # Pre-built packages (illogical-impulse-*, wallust, grub-customizer,
│ │ # snapd, upscayl, meshy-git, pacserve, etc.) │ │ # snapd, upscayl, eternalterminal, etc.)
│ └── local-aur.db{,.tar.gz} # Repo database files │ └── local-aur.db{,.tar.gz} # Repo database files
├── efiboot/ # UEFI boot (systemd-boot) ├── efiboot/ # UEFI boot (systemd-boot)
│ └── loader/ │ └── loader/
@@ -230,7 +230,7 @@ Cross-shell prompt config for [Starship](https://starship.rs/). Works with both
### **.zshrc / .fzf.zsh** ### **.zshrc / .fzf.zsh**
Zsh configuration adapted for oh-my-zsh (installed by `customize_airootfs.sh`) and fzf shell integration. Zsh configuration adapted for oh-my-zsh (installed by `customize_airootfs.sh`) and fzf shell integration. fish is the default login shell; zsh is there if you `chsh` to it. The `.zshrc` source lives at `airootfs/root/.zshrc-skel`, because grml-zsh-config owns `/etc/skel/.zshrc` and shipping it under `airootfs/etc/skel/` would abort the build. zsh-autocomplete is cloned but disabled; see [Troubleshooting](#zsh-autocomplete-partial-load-error).
--- ---
@@ -285,7 +285,7 @@ Custom PKGBUILDs for end-4 metapackages:
### Pre-built Packages ### Pre-built Packages
Packages like `illogical-impulse-quickshell-git`, `illogical-impulse-bibata-modern-classic-bin`, `wallust`, `grub-customizer`, `snapd`, `upscayl-bin`, `meshy-git`, `xwaylandvideobridge`, `pacserve`, etc. Packages like `illogical-impulse-bibata-modern-classic-bin`, `wallust`, `grub-customizer`, `snapd`, `upscayl-bin`, `xwaylandvideobridge`, `eternalterminal`, etc.
--- ---
@@ -305,6 +305,66 @@ Legacy BIOS boot with custom splash screen. Includes PXE network boot config.
--- ---
## Automated Installer (alvin-install)
The ISO ships an interactive installer at `/usr/local/bin/alvin-install` (source:
`airootfs/usr/local/bin/alvin-install`, ~1,880 lines of bash; the live `/etc/motd`
advertises it). It partitions, encrypts, installs, and fully configures a system —
including the end-4 dotfiles — without network access to this repo.
### Running it
```bash
alvin-install # as root on the live medium (root autologins on tty1)
```
Requirements: **UEFI boot** (it exits if `/sys/firmware/efi` is missing — BIOS/CSM
is not supported by the installer) and `gum`. A network connection is required and
verified by pinging `archlinux.org` before any disk is touched; the network screen
can open `nmtui` for WiFi. Every step is logged to `/var/log/alvin-install.log`,
which is copied into the installed system.
### What it asks (gum TUI screens)
Keymap → network → target disk (the live USB is hidden from the list) → LUKS
encryption (optional; **the LUKS passphrase is the same as your login password**)
→ experimental unl0kr touchscreen unlock → filesystem (`ext4`/`btrfs`) →
username/password/hostname → git identity + GitHub username (both optional) →
timezone/locale → login manager → SSH server (enabled by default) → summary.
### What it does
1. GPT partitioning (1 GiB ESP) on the chosen disk, optional LUKS2 on the rest,
format + mount at `/mnt`
2. `pacstrap` with the package list derived from the medium's
`pkglist.x86_64.txt` (`pacman -Q` of the live ISO) — `archiso`, `syslinux`,
`memtest86+` etc. are dropped, hardcoded essentials (kernel, GRUB,
NetworkManager, cryptsetup, microcode) always appended
3. User account, sudo, git config, GitHub SSH keys (`ssh-import-id`, falling
back to `github.com/<user>.keys`)
4. end-4 dotfiles from the staged `/opt/dots-hyprland` via
`./setup install -f -s --skip-alldeps`
5. Initramfs in one of two modes: **Mode A** — systemd initramfs + `sd-encrypt`
(default); **Mode B** — busybox hooks + unl0kr touchscreen unlock
(experimental, mutually exclusive with TPM2; falls back to Mode A on failure)
6. Optional **TPM2 auto-unlock** (`systemd-cryptenroll`, PCR 0+7; passphrase
slot kept as fallback; warns if Secure Boot is off)
7. GRUB (UEFI, boot entry `ALVIN`), login manager — `none` (plain tty, Hyprland
autostarts), `greetd`+tuigreet, `sddm`, or `gdm` — falls back to `none` only
if the **package** can't be resolved/installed; a failed `systemctl enable`
keeps `getty@tty1` as the login path (the summary still reports the chosen
manager). Optional autologin (forced off when TPM2 is enrolled), and sshd
enabled at boot when selected (default: on) — Arch's stock config allows
**password login with the same password as LUKS**, so only accept the default
on networks you trust (or harden `sshd_config` afterwards). The final banner
reports whether the enable actually succeeded, not just what was selected
On failure an `EXIT` trap unmounts `/mnt` and closes the `cryptroot` mapping, so
re-running is safe. If the end-4 step reports "incomplete", see the repository's
`AGENTS.md` Gotchas for the chroot/systemd limitations that cause it.
---
## Build & Test ## Build & Test
### Building the ISO ### Building the ISO
@@ -377,6 +437,56 @@ The following dotfiles are not yet included but are planned for future versions:
- `customize_airootfs.sh` clones repos at build time — requires network access during `mkarchiso` - `customize_airootfs.sh` clones repos at build time — requires network access during `mkarchiso`
- Check build logs for git clone failures - Check build logs for git clone failures
### zsh-autocomplete partial-load error
Symptom:
```
_autocomplete__history_lines: function not defined
_autocomplete__unambiguous: function not defined
```
Cause: zsh-autocomplete was loaded after `compinit` (e.g. via oh-my-zsh
`plugins=(...)`), a stale completion dump, or a conflict with
zsh-autosuggestions / zsh-history-substring-search. On this ISO `compinit`
always runs first: grml-zsh-config's global `/etc/zsh/zshrc` calls it
unconditionally before `~/.zshrc` is read.
The shipped `~/.zshrc` leaves zsh-autocomplete out of `plugins=(...)`. The
plugin stays cloned at `~/.oh-my-zsh/custom/plugins/zsh-autocomplete/`
either way, just disabled by default.
**Fix: drop autocomplete (recommended)**
```bash
# 1. Edit ~/.zshrc, remove this line from plugins=(...):
# zsh-autocomplete
# 2. Clear stale dumps (bash avoids zsh's nomatch abort on the glob):
bash -c 'rm -f ~/.zcompdump* ~/.cache/zsh/zcompdump*'
# 3. Restart the shell and verify:
exec zsh
typeset -f _autocomplete__history_lines >/dev/null && echo "still loaded" || echo "clean"
```
**Fix: keep autocomplete instead**
```bash
# 1. Remove zsh-autocomplete, zsh-autosuggestions and
# zsh-history-substring-search from plugins=(...) in ~/.zshrc.
# 2. Stop the global compinit. `skip_global_compinit=1` in ~/.zshenv does
# NOT work here (that is a Debian/Ubuntu /etc/zsh/zshrc convention; grml
# ignores it), so remove the package that ships the global zshrc:
sudo pacman -R grml-zsh-config
# 3. At the very top of ~/.zshrc, before oh-my-zsh is sourced:
source ~/.oh-my-zsh/custom/plugins/zsh-autocomplete/zsh-autocomplete.plugin.zsh
# 4. Clear dumps as above, then: exec zsh
```
--- ---
## Acknowledgements ## Acknowledgements
@@ -0,0 +1,11 @@
# mkinitcpio preset file for the 'linux-cachyos' package on archiso
# (the kernel the boot entries load). Not owned by any package: mkinitcpio's
# install hook only generates a preset when none exists, so this one wins and
# the image gets the archiso initramfs alone - no unused fallback image.
PRESETS=('archiso')
ALL_kver='/boot/vmlinuz-linux-cachyos'
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'
archiso_image="/boot/initramfs-linux-cachyos.img"
-8
View File
@@ -1,8 +0,0 @@
# mkinitcpio preset file for the 'linux' package on archiso
PRESETS=('archiso')
ALL_kver='/boot/vmlinuz-linux'
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'
archiso_image="/boot/initramfs-linux.img"
+31
View File
@@ -0,0 +1,31 @@
# ~/.zshrc - installed into /etc/skel by customize_airootfs.sh (it replaces
# grml-zsh-config's skel copy; grml's global /etc/zsh/zshrc still loads first).
export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="" # starship draws the prompt
zstyle ':omz:update' mode reminder # never auto-pull on startup
# Plugins are pre-cloned into $ZSH/custom/plugins at ISO build time.
# zsh-syntax-highlighting must load before zsh-history-substring-search.
# zsh-autocomplete is cloned too but left off: it must load before compinit,
# which grml's /etc/zsh/zshrc already ran, and it fights zsh-autosuggestions.
# To use it anyway, see README.md "zsh-autocomplete partial-load error".
plugins=(
git
fzf-zsh-plugin
zsh-eza
zsh-autosuggestions
zsh-syntax-highlighting
zsh-history-substring-search
)
source "$ZSH/oh-my-zsh.sh"
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
[[ -f ~/.fzf.zsh ]] && source ~/.fzf.zsh
if command -v starship >/dev/null 2>&1 && [[ "$TERM" != "linux" ]]; then
eval "$(starship init zsh)"
fi
+96 -11
View File
@@ -28,9 +28,13 @@ install -m 0644 /root/.live-pacman.conf /etc/pacman.conf
rm -f /root/.live-pacman.conf rm -f /root/.live-pacman.conf
# Build stamp staged by profiledef.sh (absent for non-git, non-CI builds): # Build stamp staged by profiledef.sh (absent for non-git, non-CI builds):
# keep it where alvin-install looks for it, drop the staging copy. # keep the stamp file itself as a reference, drop the staging copy, and
# stamp the live medium's motd so the booted ISO identifies its build (the
# installed system keeps the stock package motd - alvin-install never
# writes /etc/motd).
if [[ -f /root/.iso-build-stamp ]]; then if [[ -f /root/.iso-build-stamp ]]; then
install -m 0644 /root/.iso-build-stamp /etc/alvin-build-stamp install -m 0644 /root/.iso-build-stamp /etc/alvin-build-stamp
cat /etc/alvin-build-stamp >> /etc/motd
rm -f /root/.iso-build-stamp rm -f /root/.iso-build-stamp
fi fi
@@ -68,6 +72,14 @@ for plugin in "${!OMZ_PLUGINS[@]}"; do
fi fi
done done
# grml-zsh-config owns /etc/skel/.zshrc, so shipping ours in
# airootfs/etc/skel would make pacstrap abort ("exists in filesystem").
# Stage it under /root and overwrite the package's copy here, after pacstrap.
if [[ -f /root/.zshrc-skel ]]; then
install -m 0644 /root/.zshrc-skel "${SKEL}/.zshrc"
rm -f /root/.zshrc-skel
fi
# ── LazyVim (lazy.nvim bootstraps on first launch, but pre-clone the plugin) ─ # ── LazyVim (lazy.nvim bootstraps on first launch, but pre-clone the plugin) ─
NVIM_DATA="${SKEL}/.local/share/nvim" NVIM_DATA="${SKEL}/.local/share/nvim"
mkdir -p "${NVIM_DATA}" mkdir -p "${NVIM_DATA}"
@@ -85,6 +97,34 @@ if [[ ! -d "${DOTS_REPO}/.git" ]]; then
git -C "${DOTS_REPO}" submodule update --init --depth=1 git -C "${DOTS_REPO}" submodule update --init --depth=1
fi fi
# end-4 pins quickshell to 41651d7 (Sep 25), which predates quickshell's
# Qt 6.12 fix (5d5d498, Oct 3: "use MOC includes instead of opaque pointer
# definitions - Fixes linking issues and Qt 6.12 compatibility"). Arch
# shipped qt6 6.12.0 into extra on Oct 8; with the old pin qmetatype.h's
# "Meta Types must be fully defined" static_assert kills the moc build of
# quickshell-hyprland-ipc (failed CI run 137) and the whole ISO build with
# it. Force the fixed commit, but ONLY while end-4 still carries the old
# pin - once they bump it themselves the guard stops matching and their
# pin wins untouched. localrepo/check-quickshell-pin.sh (weekly CI)
# verifies both this pin and end-4's stay Qt 6.12-compatible.
QS_PKG="${DOTS_REPO}/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD"
if [[ -f "${QS_PKG}" ]] && grep -q "^_commit='41651d7" "${QS_PKG}"; then
echo "[customize] Bumping end-4 quickshell pin to 11ca60be (Qt 6.12 fix)..."
sed -i "s|^_commit='41651d7[^']*'|_commit='11ca60be22b063478ed9586ca1d7f92f0f261caf'|" "${QS_PKG}"
fi
# GCC 16.2 ICEs (segfault in ggc_set_mark) compiling MicroTeX's gtkmm_main.cpp
# - the LaTeX binary the package ships - under CachyOS's makepkg.conf
# (-march=native -O3 + -flto=auto), which killed CI run 155. Build it with -O2
# and no LTO; MicroTeX is tiny, the flags buy nothing. Re-running is a no-op.
MTX_PKG="${DOTS_REPO}/sdata/dist-arch/illogical-impulse-microtex-git/PKGBUILD"
if [[ -f "${MTX_PKG}" ]] && ! grep -q '^options=(!lto)' "${MTX_PKG}"; then
echo "[customize] Building MicroTeX with -O2 and no LTO (GCC 16 ICE)..."
# shellcheck disable=SC2016 # expanded by makepkg, not here
sed -i '/^build() {/a\ export CFLAGS="${CFLAGS//-O3/-O2}" CXXFLAGS="${CXXFLAGS//-O3/-O2}"' "${MTX_PKG}"
printf '\noptions=(!lto)\n' >> "${MTX_PKG}"
fi
# ── end4-pC: the default quickshell config, shipped in skel ────────────────── # ── end4-pC: the default quickshell config, shipped in skel ──────────────────
# pctrade/end4-pC is a fork of the ii shell config. It lives next to ii in # pctrade/end4-pC is a fork of the ii shell config. It lives next to ii in
# ~/.config/quickshell and does not modify it; which one runs is decided by # ~/.config/quickshell and does not modify it; which one runs is decided by
@@ -132,6 +172,36 @@ chmod 440 /etc/sudoers.d/99-ii-builder
# so the tree must be owned by the account that runs it. # so the tree must be owned by the account that runs it.
chown -R "${BUILD_USER}:${BUILD_USER}" "${DOTS_REPO}" chown -R "${BUILD_USER}:${BUILD_USER}" "${DOTS_REPO}"
# yay 13 (libalpm via purego/dyalpm) can SIGSEGV in alpm_release() at exit,
# *after* the transaction and its hooks completed (failed CI run: crash in
# dyalpm handle.Release). end-4 sees exit 2 and aborts. This shim, put first
# on PATH for the install-deps run only, turns a failed plain `yay -S <pkgs>`
# into success when every target is installed afterwards (pacman -T); any
# other failure keeps yay's exit code. It lives in /tmp and is removed after.
YAY_SHIM_DIR="$(mktemp -d /tmp/ii-yay-shim.XXXXXX)"
cat > "${YAY_SHIM_DIR}/yay" <<'EOF'
#!/bin/bash
"${YAY_SHIM_REAL:-/usr/bin/yay}" "$@"
rc=$?
(( rc == 0 )) && exit 0
sync=0
targets=()
for a in "$@"; do
case "$a" in
-S|--sync) sync=1 ;;
-S*) exit "$rc" ;; # -Ss/-Si/-Syu/...: not a plain install
-*) ;;
*) targets+=("$a") ;;
esac
done
if (( sync && ${#targets[@]} )) && pacman -T -- "${targets[@]}" >/dev/null; then
echo "[yay-shim] yay exited ${rc}, but all ${#targets[@]} targets are installed - continuing" >&2
exit 0
fi
exit "$rc"
EOF
chmod 755 "${YAY_SHIM_DIR}" "${YAY_SHIM_DIR}/yay"
# -f: never prompt (ask=false); -s: skip "pacman -Syu"; # -f: never prompt (ask=false); -s: skip "pacman -Syu";
# --skip-plasmaintg: optional Firefox widget that pulls 410 pkgs / 526 MiB of # --skip-plasmaintg: optional Firefox widget that pulls 410 pkgs / 526 MiB of
# KDE packages onto a non-KDE system; # KDE packages onto a non-KDE system;
@@ -140,11 +210,14 @@ chown -R "${BUILD_USER}:${BUILD_USER}" "${DOTS_REPO}"
# shellcheck disable=SC2024 # the redirect is meant for the child, not sudo # shellcheck disable=SC2024 # the redirect is meant for the child, not sudo
if ! sudo -u "${BUILD_USER}" env HOME="/home/${BUILD_USER}" \ if ! sudo -u "${BUILD_USER}" env HOME="/home/${BUILD_USER}" \
GIT_TERMINAL_PROMPT=0 \ GIT_TERMINAL_PROMPT=0 \
PATH="${YAY_SHIM_DIR}:/usr/local/sbin:/usr/local/bin:/usr/bin" \
bash -c "cd ${DOTS_REPO} && ./setup install-deps -f -s --skip-plasmaintg" \ bash -c "cd ${DOTS_REPO} && ./setup install-deps -f -s --skip-plasmaintg" \
< <(yes e); then < <(yes e); then
rm -rf "${YAY_SHIM_DIR}"
echo "[customize] end-4 ./setup install-deps failed" >&2 echo "[customize] end-4 ./setup install-deps failed" >&2
exit 1 exit 1
fi fi
rm -rf "${YAY_SHIM_DIR}"
# ── Harvest the packages install-deps just built into the in-image local repo ─ # ── Harvest the packages install-deps just built into the in-image local repo ─
# The live /etc/pacman.conf points [local-aur] at /opt/localrepo; without the # The live /etc/pacman.conf points [local-aur] at /opt/localrepo; without the
@@ -197,11 +270,13 @@ fi
# Rebuild the repo database (CI/update.sh ship a pre-built one alongside the # Rebuild the repo database (CI/update.sh ship a pre-built one alongside the
# packages) and refresh the live system's copy of it. No network needed: the # packages) and refresh the live system's copy of it. No network needed: the
# remote repos were synced during pacstrap. # remote repos were synced during pacstrap. No -R: the loop above already
# deleted the replaced archives, so repo-add's own cleanup only logged a
# spurious "Cannot remove file" ERROR.
local_pkgs=(/opt/localrepo/*.pkg.tar.zst) local_pkgs=(/opt/localrepo/*.pkg.tar.zst)
if ((${#local_pkgs[@]} > 0)); then if ((${#local_pkgs[@]} > 0)); then
echo "[customize] Rebuilding local-aur db (${#local_pkgs[@]} packages)..." echo "[customize] Rebuilding local-aur db (${#local_pkgs[@]} packages)..."
repo-add -R /opt/localrepo/local-aur.db.tar.gz "${local_pkgs[@]}" repo-add /opt/localrepo/local-aur.db.tar.gz "${local_pkgs[@]}"
mkdir -p /var/lib/pacman/sync mkdir -p /var/lib/pacman/sync
cp -fL /opt/localrepo/local-aur.db /var/lib/pacman/sync/local-aur.db cp -fL /opt/localrepo/local-aur.db /var/lib/pacman/sync/local-aur.db
if [[ -f /opt/localrepo/local-aur.files ]]; then if [[ -f /opt/localrepo/local-aur.files ]]; then
@@ -209,6 +284,16 @@ if ((${#local_pkgs[@]} > 0)); then
fi fi
fi fi
# ── mono certificate store ──────────────────────────────────────────────────
# mono's post_install runs cert-sync during pacstrap, before the
# post-transaction hook has generated ca-certificates.crt, so the store ships
# empty ("Couldn't retrieve the file") and mono apps reject every TLS cert.
if command -v cert-sync &>/dev/null; then
echo "[customize] Populating mono certificate store..."
cert-sync --quiet /etc/ssl/certs/ca-certificates.crt \
|| echo "[customize] WARNING: cert-sync failed, mono store left empty" >&2
fi
# ── makepkg leftovers + throwaway account do not belong in the ISO ────────── # ── makepkg leftovers + throwaway account do not belong in the ISO ──────────
if [[ -d "${DOTS_REPO}/sdata/dist-arch" ]]; then if [[ -d "${DOTS_REPO}/sdata/dist-arch" ]]; then
find "${DOTS_REPO}/sdata/dist-arch" -mindepth 2 -maxdepth 2 -type d \ find "${DOTS_REPO}/sdata/dist-arch" -mindepth 2 -maxdepth 2 -type d \
@@ -334,18 +419,18 @@ else
echo "[customize] uv not found - skipping python/wheel pre-warm." echo "[customize] uv not found - skipping python/wheel pre-warm."
fi fi
# ── Fish: ensure default shell is set for the live user ────────────────────── # ── Fish: default shell for the live root user ───────────────────────────────
# The live user (usually 'archlinux' or 'root') gets fish as default shell # (alvin-install creates the installed user with fish as well.)
if command -v fish &>/dev/null; then if command -v fish &>/dev/null; then
echo "[customize] Setting fish as default shell for skel..." echo "[customize] Setting fish as root's shell on the live medium..."
chsh -s /usr/bin/fish root 2>/dev/null || true chsh -s /usr/bin/fish root 2>/dev/null || true
fi fi
# ── Ensure skel directories have correct permissions ───────────────────────── # ── Ensure skel directories have correct permissions ─────────────────────────
chmod 700 "${SKEL}/.config" 2>/dev/null || true # Only take away group/other write and make everything readable: forcing
find "${SKEL}/.config" -type f -exec chmod 644 {} \; 2>/dev/null || true # files to 644 stripped +x from executables that are not *.sh (python/fish
find "${SKEL}/.config" -type d -exec chmod 755 {} \; 2>/dev/null || true # helpers in the cloned quickshell configs).
find "${SKEL}/.config" -name '*.sh' -exec chmod 755 {} \; 2>/dev/null || true chmod -R go-w,a+rX "${SKEL}/.config" "${SKEL}/.local" 2>/dev/null || true
find "${SKEL}/.local" -type d -exec chmod 755 {} \; 2>/dev/null || true find "${SKEL}/.config" -name '*.sh' -exec chmod 755 {} + 2>/dev/null || true
echo "[customize] Dotfile pre-installation complete." echo "[customize] Dotfile pre-installation complete."
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -59,8 +59,12 @@ build_local_pkgbuild() {
local pkg_name local pkg_name
pkg_name=$(basename "${pkg_dir}") pkg_name=$(basename "${pkg_dir}")
if ls "${REPO_DIR}"/illogical-impulse-"${pkg_name}"-*.pkg.tar.zst 1>/dev/null 2>&1; then # Skip only while a built package is newer than its PKGBUILD - bumping
info "Package already exists in localrepo, skipping: ${pkg_name}" # e.g. quickshell's _commit must trigger a rebuild, not ship the old one.
if [[ -n "$(find "${REPO_DIR}" -maxdepth 1 \
-name "illogical-impulse-${pkg_name}-*.pkg.tar.zst" \
-newer "${pkg_dir}/PKGBUILD" -print -quit)" ]]; then
info "Package up to date in localrepo, skipping: ${pkg_name}"
return 0 return 0
fi fi
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env bash
# Verify that every quickshell pin we build against is current enough to
# survive the next Arch Qt update.
#
# Why this exists: CI run 137 (2026-10-08) failed because end-4 pinned
# quickshell to 41651d7 (Sep 25), which predates quickshell's Qt 6.12 fix
# (5d5d498, Oct 3: "use MOC includes instead of opaque pointer definitions
# - Fixes linking issues and Qt 6.12 compatibility"). Arch shipped
# qt6 6.12.0 the next day and the pinned commit stopped compiling
# ("Meta Types must be fully defined"). customize_airootfs.sh papers over
# end-4's stale pin at build time; this script is the tripwire that tells
# us when pins - ours OR end-4's - fall behind again BEFORE a build does.
#
# Checked pins:
# 1. localrepo/end4-pkgbuilds/quickshell-git/PKGBUILD (our own pin)
# 2. end-4/dots-hyprland sdata/dist-arch PKGBUILD (live from GitHub)
#
# Checks per pin:
# - commit exists upstream
# - includes the Qt 6.12 fix (5d5d498)
# - not older than QUICKSHELL_PIN_MAX_AGE_DAYS (default 45)
#
# Exit 1 on any failure so CI (pin-check.yaml) goes red.
#
# Usage: ./localrepo/check-quickshell-pin.sh
set -euo pipefail
QT612_FIX='5d5d49873fe8cf1f99ddfd5006ceb2057c5c9b13'
QS_REPO='https://git.outfoxxed.me/quickshell/quickshell.git'
END4_STALE_PIN='41651d7' # the pin customize_airootfs.sh overrides
END4_PKGBUILD_URL='https://raw.githubusercontent.com/end-4/dots-hyprland/main/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD'
MAX_AGE_DAYS="${QUICKSHELL_PIN_MAX_AGE_DAYS:-45}"
profile_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
LOCAL_PKGBUILD="${profile_dir}/localrepo/end4-pkgbuilds/quickshell-git/PKGBUILD"
FAILED=0
fail() {
echo "❌ $1" >&2
FAILED=1
}
# ── collect pins ────────────────────────────────────────────────────────────
pin_of() { # <file-contents-stdin> -> first _commit='...' value
sed -n "s/^_commit='\([0-9a-f]\{7,40\}\)'.*/\1/p" | head -n1
}
if [[ ! -f "$LOCAL_PKGBUILD" ]]; then
echo "❌ missing ${LOCAL_PKGBUILD}" >&2
exit 1
fi
local_pin=$(pin_of < "$LOCAL_PKGBUILD")
if [[ -z "$local_pin" ]]; then
echo "❌ no _commit= found in ${LOCAL_PKGBUILD}" >&2
exit 1
fi
end4_pin=''
end4_pkgbuild=''
for try in 1 2 3 4 5; do
if end4_pkgbuild=$(curl -fsSL --max-time 30 "$END4_PKGBUILD_URL"); then
end4_pin=$(printf '%s\n' "$end4_pkgbuild" | pin_of)
break
fi
echo " ⏳ fetch of end-4 PKGBUILD failed (try $try/5), retrying in $(( try * 3 ))s..." >&2
sleep $(( try * 3 ))
done
if [[ -z "$end4_pin" ]]; then
fail "could not fetch end-4's quickshell PKGBUILD from GitHub — network or path change?"
echo ""
echo "Result: FAILED"
exit 1
fi
# ── clone quickshell (blobless: commits+trees only, fast) ───────────────────
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
cloned=0
for try in 1 2 3; do
if git clone --filter=blob:none --quiet "$QS_REPO" "$tmp/qs" 2>/dev/null; then
cloned=1
break
fi
rm -rf "$tmp/qs"
echo " ⏳ clone of quickshell failed (try $try/3), retrying in $(( try * 5 ))s..." >&2
sleep $(( try * 5 ))
done
if [[ "$cloned" -ne 1 ]]; then
fail "could not clone ${QS_REPO} — network or repo moved?"
echo ""
echo "Result: FAILED"
exit 1
fi
QS="$tmp/qs"
qs_head=$(git -C "$QS" rev-parse HEAD)
qs_head_date=$(git -C "$QS" show -s --format=%cs HEAD)
echo "quickshell upstream master: ${qs_head:0:12} (${qs_head_date})"
echo ""
now=$(date +%s)
# <label> <pin> <mode>: mode "hard" fails on any problem; mode "soft" only
# warns, for pins we don't control (end-4's) — UNLESS the problem is one the
# customize_airootfs.sh override does not cover, which always fails hard
# because it would break the next ISO build.
check_pin() {
local label=$1 pin=$2 mode=$3
local ts age behind bad=0
if ! git -C "$QS" cat-file -e "${pin}^{commit}" 2>/dev/null; then
fail "$label: pin '$pin' does not exist in quickshell upstream"
return
fi
local has_fix=0
if git -C "$QS" merge-base --is-ancestor "$QT612_FIX" "$pin"; then
has_fix=1
fi
ts=$(git -C "$QS" show -s --format=%ct "$pin")
age=$(( (now - ts) / 86400 ))
behind=$(git -C "$QS" rev-list --count "$pin..HEAD")
if [[ "$mode" == "hard" ]]; then
if [[ "$has_fix" -eq 0 ]]; then
fail "$label: pin '$pin' PREDATES the Qt 6.12 fix (${QT612_FIX:0:12}) — will not compile against Qt >= 6.12"
bad=1
fi
if (( age > MAX_AGE_DAYS )); then
fail "$label: pin is $age days old (> ${MAX_AGE_DAYS}d), $behind commit(s) behind upstream master — bump it"
bad=1
fi
if [[ "$bad" -eq 0 ]]; then
echo " ✅ $label: pin ${pin:0:12} includes the Qt 6.12 fix, age ${age}d (${behind} commit(s) behind master)"
fi
return
fi
# soft mode: our build-time override compensates for this pin, so drift is
# a heads-up — it cannot break the ISO build on its own.
if [[ "$has_fix" -eq 0 ]]; then
echo " ⚠️ $label: pin ${pin:0:12} predates the Qt 6.12 fix — covered by the customize_airootfs.sh override, but end-4 should bump it"
else
echo " ✅ $label: pin ${pin:0:12} includes the Qt 6.12 fix"
fi
if (( age > MAX_AGE_DAYS )); then
echo " ⚠️ $label: pin is $age days old (> ${MAX_AGE_DAYS}d), $behind commit(s) behind master — consider bumping (not enforced, we don't own this pin)"
else
echo " ✅ $label: pin age ${age}d (${behind} commit(s) behind master), limit ${MAX_AGE_DAYS}d"
fi
}
# ── our pin ─────────────────────────────────────────────────────────────────
echo "our pin (localrepo/end4-pkgbuilds):"
check_pin "ours" "$local_pin" hard
# ── end-4's pin ─────────────────────────────────────────────────────────────
echo ""
echo "end-4's pin (dots-hyprland sdata/dist-arch):"
# end-4's pin is only safe if OUR override covers it: customize_airootfs.sh
# rewrites exactly the 41651d7 pin. Any other Qt-unsafe end-4 pin sails
# straight into the ISO build unpatched → hard failure.
if [[ "$end4_pin" == "$END4_STALE_PIN"* ]]; then
echo " ℹ️ end-4 still pins ${end4_pin:0:12} — customize_airootfs.sh override is active"
check_pin "end-4" "$end4_pin" soft
else
check_pin "end-4" "$end4_pin" hard
echo " ℹ️ end-4 has moved off ${END4_STALE_PIN} to ${end4_pin:0:12}"
echo " → if it now passes, consider retiring the override guard in"
echo " customize_airootfs.sh and syncing our own pin to match."
fi
echo ""
if [[ "$FAILED" -ne 0 ]]; then
echo "Result: FAILED"
exit 1
fi
echo "Result: OK — all quickshell pins are Qt 6.12-safe and within ${MAX_AGE_DAYS}d of upstream"
@@ -18,6 +18,8 @@ depends=(
makedepends=("git" "cmake") makedepends=("git" "cmake")
source=("git+${url}.git") source=("git+${url}.git")
sha256sums=("SKIP") sha256sums=("SKIP")
# -O3 + LTO (CachyOS makepkg.conf) make GCC 16.2 ICE on gtkmm_main.cpp
options=(!lto)
prepare() { prepare() {
cd $_pkgname cd $_pkgname
@@ -26,6 +28,7 @@ prepare() {
} }
build() { build() {
export CFLAGS="${CFLAGS//-O3/-O2}" CXXFLAGS="${CXXFLAGS//-O3/-O2}"
cd $_pkgname cd $_pkgname
cmake -B build -S . -DCMAKE_BUILD_TYPE=None cmake -B build -S . -DCMAKE_BUILD_TYPE=None
cmake --build build cmake --build build
@@ -1,4 +1,4 @@
_commit='7511545ee20664e3b8b8d3322c0ffe7567c56f7a' _commit='11ca60be22b063478ed9586ca1d7f92f0f261caf'
# Useful links: # Useful links:
# https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master # https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master
# https://aur.archlinux.org/packages/quickshell-git # https://aur.archlinux.org/packages/quickshell-git
@@ -8,7 +8,7 @@ _prefix='illogical-impulse'
_pkgname=quickshell _pkgname=quickshell
pkgname="$_prefix-$_pkgname-git" pkgname="$_prefix-$_pkgname-git"
pkgver=0.1.0.r1 pkgver=0.1.0.r1
pkgrel=8 pkgrel=9
pkgdesc="$_pkgname-git pinned commit and extra deps for $_prefix" pkgdesc="$_pkgname-git pinned commit and extra deps for $_prefix"
arch=(x86_64 aarch64) arch=(x86_64 aarch64)
url='https://git.outfoxxed.me/quickshell/quickshell' url='https://git.outfoxxed.me/quickshell/quickshell'
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -39,7 +39,8 @@ rm -f "${img_repo}"/*.pkg.tar.zst "${img_repo}/${repo_name}".db \
"${img_repo}/${repo_name}".db.tar.gz "${img_repo}/${repo_name}".files \ "${img_repo}/${repo_name}".db.tar.gz "${img_repo}/${repo_name}".files \
"${img_repo}/${repo_name}".files.tar.gz "${img_repo}/${repo_name}".files.tar.gz
cp -f "${pkgs[@]}" "${img_repo}/" cp -f "${pkgs[@]}" "${img_repo}/"
cp -fL "${repo_dir}/${repo_name}".db* "${repo_dir}/${repo_name}".files* "${img_repo}/" # explicit names: a .db* glob would also ship repo-add's *.tar.gz.old backups
cp -fL "${repo_dir}/${repo_name}".{db,db.tar.gz,files,files.tar.gz} "${img_repo}/"
echo "Medium copy ready: ${img_repo}" echo "Medium copy ready: ${img_repo}"
new_names=() new_names=()
+38 -23
View File
@@ -3,8 +3,8 @@ alsa-utils
amd-ucode amd-ucode
ansible ansible
archinstall archinstall
archlinux-keyring
arch-install-scripts arch-install-scripts
archlinux-keyring
asciinema asciinema
asciinema-agg asciinema-agg
ast-grep ast-grep
@@ -24,20 +24,21 @@ bolt
brightnessctl brightnessctl
brltty brltty
btop btop
bullet
btrfs-progs btrfs-progs
bullet
bully bully
cachyos-keyring cachyos-keyring
cachyos-mirrorlist cachyos-mirrorlist
catppuccin-cursors-macchiato
cava cava
celt celt
chaotic-keyring chaotic-keyring
chaotic-mirrorlist chaotic-mirrorlist
cifs-utils cifs-utils
clamav clamav
cliphist
clonezilla clonezilla
cloud-init cloud-init
cliphist
cmake cmake
cmatrix cmatrix
code code
@@ -50,13 +51,13 @@ curl
darkhttpd darkhttpd
davs2 davs2
dconf-editor dconf-editor
ddcutil
ddrescue ddrescue
dhcpcd dhcpcd
dialog dialog
diffutils diffutils
dmidecode dmidecode
dmraid dmraid
ddcutil
dnsmasq dnsmasq
dolphin-plugins dolphin-plugins
dosfstools dosfstools
@@ -68,14 +69,12 @@ efibootmgr
electron electron
element-desktop element-desktop
espeakup espeakup
eternalterminal
ethtool ethtool
exfatprogs exfatprogs
f2fs-tools f2fs-tools
faac faac
faad2 faad2
fish
flac
fontconfig
fastfetch fastfetch
fatresize fatresize
fcitx5 fcitx5
@@ -88,8 +87,11 @@ ffmpegthumbnailer
ffmpegthumbs ffmpegthumbs
figlet figlet
firefox firefox
fish
flac
flatpak flatpak
foliate foliate
fontconfig
foot-terminfo foot-terminfo
foremost foremost
fortune-mod fortune-mod
@@ -97,22 +99,23 @@ fprintd
fsarchiver fsarchiver
fuse-overlayfs fuse-overlayfs
fzf fzf
geoclue
ghostty ghostty
ghostty-terminfo ghostty-terminfo
git git
geoclue
glib2-devel glib2-devel
gnome-disk-utility gnome-disk-utility
gnome-font-viewer gnome-font-viewer
gnome-online-accounts gnome-online-accounts
gnome-power-manager gnome-power-manager
go-yq
gpart gpart
gparted gparted
go-yq
gpm gpm
gptfdisk gptfdisk
grml-zsh-config grml-zsh-config
grub grub
grub-customizer
gst-plugins-base gst-plugins-base
gst-plugins-ugly gst-plugins-ugly
gst-plugin-va gst-plugin-va
@@ -130,11 +133,12 @@ htop
hyperv hyperv
hypridle hypridle
hyprlock hyprlock
hyprpolkitagent
hyprpicker hyprpicker
hyprpolkitagent
hyprshot hyprshot
hyprsunset hyprsunset
icu icu
illogical-impulse-bibata-modern-classic-bin
intel-ucode intel-ucode
inxi inxi
irssi irssi
@@ -163,18 +167,18 @@ kwalletmanager
lazygit lazygit
ldns ldns
less less
lftp
libass libass
libdbusmenu-gtk3 libdbusmenu-gtk3
libdecor libdecor
libevdev libevdev
libportal
libportal-gtk4
libqalculate
libpipewire
lftp
libfido2 libfido2
libfprint libfprint
libisoburn libisoburn
libpipewire
libportal
libportal-gtk4
libqalculate
libtorrent libtorrent
libusb-compat libusb-compat
linux-atm linux-atm
@@ -213,14 +217,14 @@ mplayer
mpv-mpris mpv-mpris
mtools mtools
nano nano
ncurses
nbd nbd
ncurses
ndisc6 ndisc6
neofetch neofetch
neovim neovim
net-tools net-tools
network-manager-applet
networkmanager networkmanager
network-manager-applet
nfs-utils nfs-utils
nikto nikto
nilfs-utils nilfs-utils
@@ -267,22 +271,23 @@ ppp
pptpclient pptpclient
pv pv
pwgen pwgen
python-aiohttp
python-distro
python-hatchling python-hatchling
python-installer python-installer
python-packaging
python-pip python-pip
python-pyqt6 python-pyqt6
python-pyquery python-pyquery
python-ufonormalizer python-ufonormalizer
python-uv python-uv
python-virtualenv python-virtualenv
python-aiohttp
python-packaging
qalculate-gtk qalculate-gtk
qemu-full qemu-full
qemu-guest-agent qemu-guest-agent
qt5-quickcontrols2 qt5-quickcontrols2
qt6ct
qt6-5compat qt6-5compat
qt6ct
qt6-imageformats qt6-imageformats
qt6-multimedia qt6-multimedia
qt6-positioning qt6-positioning
@@ -295,6 +300,7 @@ qt6-wayland
rclone rclone
refind refind
reflector reflector
rpi-imager-git
rsync rsync
rxvt-unicode-terminfo rxvt-unicode-terminfo
samba samba
@@ -312,12 +318,14 @@ shelly
shelly-flatpak-backend shelly-flatpak-backend
shfmt shfmt
smartmontools smartmontools
snapd
sof-firmware sof-firmware
sox
songrec songrec
sox
speech-dispatcher speech-dispatcher
squashfs-tools squashfs-tools
sshfs sshfs
ssh-import-id
starship starship
sudo sudo
swaync swaync
@@ -357,6 +365,7 @@ ttf-material-symbols-variable-git
ttf-ms-fonts ttf-ms-fonts
ttf-readex-pro ttf-readex-pro
ttf-twemoji ttf-twemoji
ttf-victor-mono
tty-clock tty-clock
tumbler tumbler
udftools udftools
@@ -364,6 +373,7 @@ uget
umockdev umockdev
unrar unrar
upower upower
upscayl-bin
uriparser uriparser
usbguard usbguard
usb_modeswitch usb_modeswitch
@@ -377,6 +387,10 @@ vim
virt-manager virt-manager
virtualbox-guest-utils-nox virtualbox-guest-utils-nox
vpnc vpnc
vulkan-intel
vulkan-nouveau
vulkan-radeon
wallust
wayland-protocols wayland-protocols
wine wine
winetricks winetricks
@@ -384,15 +398,16 @@ wireless-regdb
wireless_tools wireless_tools
woeusb-ng woeusb-ng
wpa_supplicant wpa_supplicant
wvdial
wtype wtype
wvdial
xarchiver xarchiver
xclip xclip
xdg-utils
xdg-user-dirs xdg-user-dirs
xdg-utils
xdotool xdotool
xfsprogs xfsprogs
xl2tpd xl2tpd
xwaylandvideobridge
yay yay
ydotool ydotool
yt-dlp yt-dlp
-2
View File
@@ -76,7 +76,6 @@ LocalFileSigLevel = Optional
#Include = /etc/pacman.d/mirrorlist #Include = /etc/pacman.d/mirrorlist
[cachyos] [cachyos]
SigLevel = Optional TrustAll
Include = /etc/pacman.d/cachyos-mirrorlist Include = /etc/pacman.d/cachyos-mirrorlist
[core] [core]
@@ -98,7 +97,6 @@ Include = /etc/pacman.d/mirrorlist
Include = /etc/pacman.d/mirrorlist Include = /etc/pacman.d/mirrorlist
[chaotic-aur] [chaotic-aur]
SigLevel = Optional TrustAll
Include = /etc/pacman.d/chaotic-mirrorlist Include = /etc/pacman.d/chaotic-mirrorlist
[local-aur] [local-aur]
+3 -2
View File
@@ -27,8 +27,9 @@ file_permissions=(
# ── Build stamp (staged file, never committed) ──────────────────────────────── # ── Build stamp (staged file, never committed) ────────────────────────────────
# airootfs/root/.iso-build-stamp lands at /root/.iso-build-stamp on the ISO; # airootfs/root/.iso-build-stamp lands at /root/.iso-build-stamp on the ISO;
# customize_airootfs.sh installs it as /etc/alvin-build-stamp, and alvin-install # customize_airootfs.sh keeps it as /etc/alvin-build-stamp and appends it to
# appends it to the installed system's /etc/motd. Only stamp builds from a git # the live medium's /etc/motd (the installed system's motd is left alone).
# Only stamp builds from a git
# checkout or CI — a random source-tarball build would record a bogus commit. # checkout or CI — a random source-tarball build would record a bogus commit.
# Sourced by mkarchiso under `set -eu`, hence every expansion is defaulted. # Sourced by mkarchiso under `set -eu`, hence every expansion is defaulted.
_stamp_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" _stamp_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"